ASR_BASE

Change-Id: Icf3719cc0afe3eeb3edc7fa80a2eb5199ca9dda1
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/Makefile b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/Makefile
new file mode 100644
index 0000000..d995b81
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/Makefile
@@ -0,0 +1,30 @@
+#CFLAGS += -DWPA_TRACE
+CFLAGS += -DCONFIG_IPV6
+CFLAGS += -DCONFIG_DEBUG_FILE
+
+LIB_OBJS= \
+	base64.o \
+	bitfield.o \
+	common.o \
+	config.o \
+	crc32.o \
+	ip_addr.o \
+	json.o \
+	radiotap.o \
+	trace.o \
+	uuid.o \
+	wpa_debug.o \
+	wpabuf.o
+
+# Pick correct OS wrapper implementation
+LIB_OBJS += os_unix.o
+
+# Pick correct event loop implementation
+LIB_OBJS += eloop.o
+
+# Pick correct edit implementation
+LIB_OBJS += edit.o
+
+#LIB_OBJS += pcsc_funcs.o
+
+include ../lib.rules
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/base64.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/base64.c
new file mode 100644
index 0000000..0d121c1
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/base64.c
@@ -0,0 +1,208 @@
+/*
+ * Base64 encoding/decoding (RFC1341)
+ * Copyright (c) 2005-2019, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <stdint.h>
+
+#include "utils/common.h"
+#include "os.h"
+#include "base64.h"
+
+static const char base64_table[65] =
+	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+static const char base64_url_table[65] =
+	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
+
+
+#define BASE64_PAD BIT(0)
+#define BASE64_LF BIT(1)
+
+
+static char * base64_gen_encode(const unsigned char *src, size_t len,
+				size_t *out_len, const char *table, int add_pad)
+{
+	char *out, *pos;
+	const unsigned char *end, *in;
+	size_t olen;
+	int line_len;
+
+	if (len >= SIZE_MAX / 4)
+		return NULL;
+	olen = len * 4 / 3 + 4; /* 3-byte blocks to 4-byte */
+	if (add_pad & BASE64_LF)
+		olen += olen / 72; /* line feeds */
+	olen++; /* nul termination */
+	if (olen < len)
+		return NULL; /* integer overflow */
+	out = os_malloc(olen);
+	if (out == NULL)
+		return NULL;
+
+	end = src + len;
+	in = src;
+	pos = out;
+	line_len = 0;
+	while (end - in >= 3) {
+		*pos++ = table[(in[0] >> 2) & 0x3f];
+		*pos++ = table[(((in[0] & 0x03) << 4) | (in[1] >> 4)) & 0x3f];
+		*pos++ = table[(((in[1] & 0x0f) << 2) | (in[2] >> 6)) & 0x3f];
+		*pos++ = table[in[2] & 0x3f];
+		in += 3;
+		line_len += 4;
+		if ((add_pad & BASE64_LF) && line_len >= 72) {
+			*pos++ = '\n';
+			line_len = 0;
+		}
+	}
+
+	if (end - in) {
+		*pos++ = table[(in[0] >> 2) & 0x3f];
+		if (end - in == 1) {
+			*pos++ = table[((in[0] & 0x03) << 4) & 0x3f];
+			if (add_pad & BASE64_PAD)
+				*pos++ = '=';
+		} else {
+			*pos++ = table[(((in[0] & 0x03) << 4) |
+					(in[1] >> 4)) & 0x3f];
+			*pos++ = table[((in[1] & 0x0f) << 2) & 0x3f];
+		}
+		if (add_pad & BASE64_PAD)
+			*pos++ = '=';
+		line_len += 4;
+	}
+
+	if ((add_pad & BASE64_LF) && line_len)
+		*pos++ = '\n';
+
+	*pos = '\0';
+	if (out_len)
+		*out_len = pos - out;
+	return out;
+}
+
+
+static unsigned char * base64_gen_decode(const char *src, size_t len,
+					 size_t *out_len, const char *table)
+{
+	unsigned char dtable[256], *out, *pos, block[4], tmp;
+	size_t i, count, olen;
+	int pad = 0;
+	size_t extra_pad;
+
+	os_memset(dtable, 0x80, 256);
+	for (i = 0; i < sizeof(base64_table) - 1; i++)
+		dtable[(unsigned char) table[i]] = (unsigned char) i;
+	dtable['='] = 0;
+
+	count = 0;
+	for (i = 0; i < len; i++) {
+		if (dtable[(unsigned char) src[i]] != 0x80)
+			count++;
+	}
+
+	if (count == 0)
+		return NULL;
+	extra_pad = (4 - count % 4) % 4;
+
+	olen = (count + extra_pad) / 4 * 3;
+	pos = out = os_malloc(olen);
+	if (out == NULL)
+		return NULL;
+
+	count = 0;
+	for (i = 0; i < len + extra_pad; i++) {
+		unsigned char val;
+
+		if (i >= len)
+			val = '=';
+		else
+			val = src[i];
+		tmp = dtable[val];
+		if (tmp == 0x80)
+			continue;
+
+		if (val == '=')
+			pad++;
+		block[count] = tmp;
+		count++;
+		if (count == 4) {
+			*pos++ = (block[0] << 2) | (block[1] >> 4);
+			*pos++ = (block[1] << 4) | (block[2] >> 2);
+			*pos++ = (block[2] << 6) | block[3];
+			count = 0;
+			if (pad) {
+				if (pad == 1)
+					pos--;
+				else if (pad == 2)
+					pos -= 2;
+				else {
+					/* Invalid padding */
+					os_free(out);
+					return NULL;
+				}
+				break;
+			}
+		}
+	}
+
+	*out_len = pos - out;
+	return out;
+}
+
+
+/**
+ * base64_encode - Base64 encode
+ * @src: Data to be encoded
+ * @len: Length of the data to be encoded
+ * @out_len: Pointer to output length variable, or %NULL if not used
+ * Returns: Allocated buffer of out_len bytes of encoded data,
+ * or %NULL on failure
+ *
+ * Caller is responsible for freeing the returned buffer. Returned buffer is
+ * nul terminated to make it easier to use as a C string. The nul terminator is
+ * not included in out_len.
+ */
+char * base64_encode(const void *src, size_t len, size_t *out_len)
+{
+	return base64_gen_encode(src, len, out_len, base64_table,
+				 BASE64_PAD | BASE64_LF);
+}
+
+
+char * base64_encode_no_lf(const void *src, size_t len, size_t *out_len)
+{
+	return base64_gen_encode(src, len, out_len, base64_table, BASE64_PAD);
+}
+
+
+char * base64_url_encode(const void *src, size_t len, size_t *out_len)
+{
+	return base64_gen_encode(src, len, out_len, base64_url_table, 0);
+}
+
+
+/**
+ * base64_decode - Base64 decode
+ * @src: Data to be decoded
+ * @len: Length of the data to be decoded
+ * @out_len: Pointer to output length variable
+ * Returns: Allocated buffer of out_len bytes of decoded data,
+ * or %NULL on failure
+ *
+ * Caller is responsible for freeing the returned buffer.
+ */
+unsigned char * base64_decode(const char *src, size_t len, size_t *out_len)
+{
+	return base64_gen_decode(src, len, out_len, base64_table);
+}
+
+
+unsigned char * base64_url_decode(const char *src, size_t len, size_t *out_len)
+{
+	return base64_gen_decode(src, len, out_len, base64_url_table);
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/base64.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/base64.h
new file mode 100644
index 0000000..d545b29
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/base64.h
@@ -0,0 +1,18 @@
+/*
+ * Base64 encoding/decoding (RFC1341)
+ * Copyright (c) 2005, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef BASE64_H
+#define BASE64_H
+
+char * base64_encode(const void *src, size_t len, size_t *out_len);
+char * base64_encode_no_lf(const void *src, size_t len, size_t *out_len);
+unsigned char * base64_decode(const char *src, size_t len, size_t *out_len);
+char * base64_url_encode(const void *src, size_t len, size_t *out_len);
+unsigned char * base64_url_decode(const char *src, size_t len, size_t *out_len);
+
+#endif /* BASE64_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/bitfield.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/bitfield.c
new file mode 100644
index 0000000..8dcec39
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/bitfield.c
@@ -0,0 +1,89 @@
+/*
+ * Bitfield
+ * Copyright (c) 2013, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "bitfield.h"
+
+
+struct bitfield {
+	u8 *bits;
+	size_t max_bits;
+};
+
+
+struct bitfield * bitfield_alloc(size_t max_bits)
+{
+	struct bitfield *bf;
+
+	bf = os_zalloc(sizeof(*bf) + (max_bits + 7) / 8);
+	if (bf == NULL)
+		return NULL;
+	bf->bits = (u8 *) (bf + 1);
+	bf->max_bits = max_bits;
+	return bf;
+}
+
+
+void bitfield_free(struct bitfield *bf)
+{
+	os_free(bf);
+}
+
+
+void bitfield_set(struct bitfield *bf, size_t bit)
+{
+	if (bit >= bf->max_bits)
+		return;
+	bf->bits[bit / 8] |= BIT(bit % 8);
+}
+
+
+void bitfield_clear(struct bitfield *bf, size_t bit)
+{
+	if (bit >= bf->max_bits)
+		return;
+	bf->bits[bit / 8] &= ~BIT(bit % 8);
+}
+
+
+int bitfield_is_set(struct bitfield *bf, size_t bit)
+{
+	if (bit >= bf->max_bits)
+		return 0;
+	return !!(bf->bits[bit / 8] & BIT(bit % 8));
+}
+
+
+static int first_zero(u8 val)
+{
+	int i;
+	for (i = 0; i < 8; i++) {
+		if (!(val & 0x01))
+			return i;
+		val >>= 1;
+	}
+	return -1;
+}
+
+
+int bitfield_get_first_zero(struct bitfield *bf)
+{
+	size_t i;
+	for (i = 0; i < (bf->max_bits + 7) / 8; i++) {
+		if (bf->bits[i] != 0xff)
+			break;
+	}
+	if (i == (bf->max_bits + 7) / 8)
+		return -1;
+	i = i * 8 + first_zero(bf->bits[i]);
+	if (i >= bf->max_bits)
+		return -1;
+	return i;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/bitfield.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/bitfield.h
new file mode 100644
index 0000000..7050a20
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/bitfield.h
@@ -0,0 +1,21 @@
+/*
+ * Bitfield
+ * Copyright (c) 2013, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef BITFIELD_H
+#define BITFIELD_H
+
+struct bitfield;
+
+struct bitfield * bitfield_alloc(size_t max_bits);
+void bitfield_free(struct bitfield *bf);
+void bitfield_set(struct bitfield *bf, size_t bit);
+void bitfield_clear(struct bitfield *bf, size_t bit);
+int bitfield_is_set(struct bitfield *bf, size_t bit);
+int bitfield_get_first_zero(struct bitfield *bf);
+
+#endif /* BITFIELD_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/browser-android.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/browser-android.c
new file mode 100644
index 0000000..26c83d6
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/browser-android.c
@@ -0,0 +1,126 @@
+/*
+ * Hotspot 2.0 client - Web browser using Android browser
+ * Copyright (c) 2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "utils/eloop.h"
+#include "wps/http_server.h"
+#include "browser.h"
+
+
+struct browser_data {
+	int success;
+};
+
+
+static void browser_timeout(void *eloop_data, void *user_ctx)
+{
+	wpa_printf(MSG_INFO, "Timeout on waiting browser interaction to "
+		   "complete");
+	eloop_terminate();
+}
+
+
+static void http_req(void *ctx, struct http_request *req)
+{
+	struct browser_data *data = ctx;
+	struct wpabuf *resp;
+	const char *url;
+	int done = 0;
+
+	url = http_request_get_uri(req);
+	wpa_printf(MSG_INFO, "Browser response received: %s", url);
+
+	if (os_strcmp(url, "/") == 0) {
+		data->success = 1;
+		done = 1;
+	} else if (os_strncmp(url, "/osu/", 5) == 0) {
+		data->success = atoi(url + 5);
+		done = 1;
+	}
+
+	resp = wpabuf_alloc(1);
+	if (resp == NULL) {
+		http_request_deinit(req);
+		if (done)
+			eloop_terminate();
+		return;
+	}
+
+	if (done) {
+		eloop_cancel_timeout(browser_timeout, NULL, NULL);
+		eloop_register_timeout(0, 500000, browser_timeout, &data, NULL);
+	}
+
+	http_request_send_and_deinit(req, resp);
+}
+
+
+int hs20_web_browser(const char *url, int ignore_tls)
+{
+	struct http_server *http;
+	struct in_addr addr;
+	struct browser_data data;
+	pid_t pid;
+
+	wpa_printf(MSG_INFO, "Launching Android browser to %s", url);
+
+	os_memset(&data, 0, sizeof(data));
+
+	if (eloop_init() < 0) {
+		wpa_printf(MSG_ERROR, "eloop_init failed");
+		return -1;
+	}
+	addr.s_addr = htonl((127 << 24) | 1);
+	http = http_server_init(&addr, 12345, http_req, &data);
+	if (http == NULL) {
+		wpa_printf(MSG_ERROR, "http_server_init failed");
+		eloop_destroy();
+		return -1;
+	}
+
+	pid = fork();
+	if (pid < 0) {
+		wpa_printf(MSG_ERROR, "fork: %s", strerror(errno));
+		http_server_deinit(http);
+		eloop_destroy();
+		return -1;
+	}
+
+	if (pid == 0) {
+		/* run the external command in the child process */
+		char *argv[7];
+
+		argv[0] = "browser-android";
+		argv[1] = "start";
+		argv[2] = "-a";
+		argv[3] = "android.intent.action.VIEW";
+		argv[4] = "-d";
+		argv[5] = (void *) url;
+		argv[6] = NULL;
+
+		execv("/system/bin/am", argv);
+		wpa_printf(MSG_ERROR, "execv: %s", strerror(errno));
+		exit(0);
+		return -1;
+	}
+
+	eloop_register_timeout(30, 0, browser_timeout, &data, NULL);
+	eloop_run();
+	eloop_cancel_timeout(browser_timeout, &data, NULL);
+	http_server_deinit(http);
+	eloop_destroy();
+
+	wpa_printf(MSG_INFO, "Closing Android browser");
+	if (system("/system/bin/input keyevent KEYCODE_HOME") != 0) {
+		wpa_printf(MSG_INFO, "Failed to inject keyevent");
+	}
+
+	return data.success;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/browser-system.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/browser-system.c
new file mode 100644
index 0000000..d87d97b
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/browser-system.c
@@ -0,0 +1,119 @@
+/*
+ * Hotspot 2.0 client - Web browser using system browser
+ * Copyright (c) 2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "utils/eloop.h"
+#include "wps/http_server.h"
+#include "browser.h"
+
+
+struct browser_data {
+	int success;
+};
+
+
+static void browser_timeout(void *eloop_data, void *user_ctx)
+{
+	wpa_printf(MSG_INFO, "Timeout on waiting browser interaction to "
+		   "complete");
+	eloop_terminate();
+}
+
+
+static void http_req(void *ctx, struct http_request *req)
+{
+	struct browser_data *data = ctx;
+	struct wpabuf *resp;
+	const char *url;
+	int done = 0;
+
+	url = http_request_get_uri(req);
+	wpa_printf(MSG_INFO, "Browser response received: %s", url);
+
+	if (os_strcmp(url, "/") == 0) {
+		data->success = 1;
+		done = 1;
+	} else if (os_strncmp(url, "/osu/", 5) == 0) {
+		data->success = atoi(url + 5);
+		done = 1;
+	}
+
+	resp = wpabuf_alloc(1);
+	if (resp == NULL) {
+		http_request_deinit(req);
+		if (done)
+			eloop_terminate();
+		return;
+	}
+
+	if (done) {
+		eloop_cancel_timeout(browser_timeout, NULL, NULL);
+		eloop_register_timeout(0, 500000, browser_timeout, &data, NULL);
+	}
+
+	http_request_send_and_deinit(req, resp);
+}
+
+
+int hs20_web_browser(const char *url, int ignore_tls)
+{
+	struct http_server *http;
+	struct in_addr addr;
+	struct browser_data data;
+	pid_t pid;
+
+	wpa_printf(MSG_INFO, "Launching system browser to %s", url);
+
+	os_memset(&data, 0, sizeof(data));
+
+	if (eloop_init() < 0) {
+		wpa_printf(MSG_ERROR, "eloop_init failed");
+		return -1;
+	}
+	addr.s_addr = htonl((127 << 24) | 1);
+	http = http_server_init(&addr, 12345, http_req, &data);
+	if (http == NULL) {
+		wpa_printf(MSG_ERROR, "http_server_init failed");
+		eloop_destroy();
+		return -1;
+	}
+
+	pid = fork();
+	if (pid < 0) {
+		wpa_printf(MSG_ERROR, "fork: %s", strerror(errno));
+		http_server_deinit(http);
+		eloop_destroy();
+		return -1;
+	}
+
+	if (pid == 0) {
+		/* run the external command in the child process */
+		char *argv[3];
+
+		argv[0] = "browser-system";
+		argv[1] = (void *) url;
+		argv[2] = NULL;
+
+		execv("/usr/bin/x-www-browser", argv);
+		wpa_printf(MSG_ERROR, "execv: %s", strerror(errno));
+		exit(0);
+		return -1;
+	}
+
+	eloop_register_timeout(120, 0, browser_timeout, &data, NULL);
+	eloop_run();
+	eloop_cancel_timeout(browser_timeout, &data, NULL);
+	http_server_deinit(http);
+	eloop_destroy();
+
+	/* TODO: Close browser */
+
+	return data.success;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/browser-wpadebug.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/browser-wpadebug.c
new file mode 100644
index 0000000..d32a85b
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/browser-wpadebug.c
@@ -0,0 +1,139 @@
+/*
+ * Hotspot 2.0 client - Web browser using wpadebug on Android
+ * Copyright (c) 2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "utils/eloop.h"
+#include "wps/http_server.h"
+#include "browser.h"
+
+
+struct browser_data {
+	int success;
+};
+
+
+static void browser_timeout(void *eloop_data, void *user_ctx)
+{
+	wpa_printf(MSG_INFO, "Timeout on waiting browser interaction to "
+		   "complete");
+	eloop_terminate();
+}
+
+
+static void http_req(void *ctx, struct http_request *req)
+{
+	struct browser_data *data = ctx;
+	struct wpabuf *resp;
+	const char *url;
+	int done = 0;
+
+	url = http_request_get_uri(req);
+	wpa_printf(MSG_INFO, "Browser response received: %s", url);
+
+	if (os_strcmp(url, "/") == 0) {
+		data->success = 1;
+		done = 1;
+	} else if (os_strncmp(url, "/osu/", 5) == 0) {
+		data->success = atoi(url + 5);
+		done = 1;
+	}
+
+	resp = wpabuf_alloc(100);
+	if (resp == NULL) {
+		http_request_deinit(req);
+		if (done)
+			eloop_terminate();
+		return;
+	}
+	wpabuf_put_str(resp, "HTTP/1.1\r\n\r\nUser input completed");
+
+	if (done) {
+		eloop_cancel_timeout(browser_timeout, NULL, NULL);
+		eloop_register_timeout(0, 500000, browser_timeout, &data, NULL);
+	}
+
+	http_request_send_and_deinit(req, resp);
+}
+
+
+int hs20_web_browser(const char *url, int ignore_tls)
+{
+	struct http_server *http;
+	struct in_addr addr;
+	struct browser_data data;
+	pid_t pid;
+
+	wpa_printf(MSG_INFO, "Launching wpadebug browser to %s", url);
+
+	os_memset(&data, 0, sizeof(data));
+
+	if (eloop_init() < 0) {
+		wpa_printf(MSG_ERROR, "eloop_init failed");
+		return -1;
+	}
+	addr.s_addr = htonl((127 << 24) | 1);
+	http = http_server_init(&addr, 12345, http_req, &data);
+	if (http == NULL) {
+		wpa_printf(MSG_ERROR, "http_server_init failed");
+		eloop_destroy();
+		return -1;
+	}
+
+	pid = fork();
+	if (pid < 0) {
+		wpa_printf(MSG_ERROR, "fork: %s", strerror(errno));
+		http_server_deinit(http);
+		eloop_destroy();
+		return -1;
+	}
+
+	if (pid == 0) {
+		/* run the external command in the child process */
+		char *argv[14];
+		char *envp[] = { "PATH=/system/bin:/vendor/bin", NULL };
+
+		argv[0] = "browser-wpadebug";
+		argv[1] = "start";
+		argv[2] = "-a";
+		argv[3] = "android.action.MAIN";
+		argv[4] = "-c";
+		argv[5] = "android.intent.category.LAUNCHER";
+		argv[6] = "-n";
+		argv[7] = "w1.fi.wpadebug/.WpaWebViewActivity";
+		argv[8] = "-e";
+		argv[9] = "w1.fi.wpadebug.URL";
+		argv[10] = (void *) url;
+		argv[11] = "--user";
+		argv[12] = "-3"; /* USER_CURRENT_OR_SELF */
+		argv[13] = NULL;
+
+		execve("/system/bin/am", argv, envp);
+		wpa_printf(MSG_ERROR, "execve: %s", strerror(errno));
+		exit(0);
+		return -1;
+	}
+
+	eloop_register_timeout(300, 0, browser_timeout, &data, NULL);
+	eloop_run();
+	eloop_cancel_timeout(browser_timeout, &data, NULL);
+	http_server_deinit(http);
+	eloop_destroy();
+
+	wpa_printf(MSG_INFO, "Closing Android browser");
+	if (os_exec("/system/bin/am",
+		    "start -a android.action.MAIN "
+		    "-c android.intent.category.LAUNCHER "
+		    "-n w1.fi.wpadebug/.WpaWebViewActivity "
+		    "-e w1.fi.wpadebug.URL FINISH", 1) != 0) {
+		wpa_printf(MSG_INFO, "Failed to close wpadebug browser");
+	}
+
+	return data.success;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/browser.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/browser.c
new file mode 100644
index 0000000..b5d5ba7
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/browser.c
@@ -0,0 +1,402 @@
+/*
+ * Hotspot 2.0 client - Web browser using WebKit
+ * Copyright (c) 2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#ifdef USE_WEBKIT2
+#include <webkit2/webkit2.h>
+#else /* USE_WEBKIT2 */
+#include <webkit/webkit.h>
+#endif /* USE_WEBKIT2 */
+
+#include "common.h"
+#include "browser.h"
+
+
+struct browser_context {
+	GtkWidget *win;
+	WebKitWebView *view;
+	int success;
+	int progress;
+	char *hover_link;
+	char *title;
+	int gtk_main_started;
+	int quit_gtk_main;
+};
+
+static void win_cb_destroy(GtkWidget *win, struct browser_context *ctx)
+{
+	wpa_printf(MSG_DEBUG, "BROWSER:%s", __func__);
+	if (ctx->gtk_main_started)
+		gtk_main_quit();
+}
+
+
+static void browser_update_title(struct browser_context *ctx)
+{
+	char buf[100];
+
+	if (ctx->hover_link) {
+		gtk_window_set_title(GTK_WINDOW(ctx->win), ctx->hover_link);
+		return;
+	}
+
+	if (ctx->progress == 100) {
+		gtk_window_set_title(GTK_WINDOW(ctx->win),
+				     ctx->title ? ctx->title :
+				     "Hotspot 2.0 client");
+		return;
+	}
+
+	snprintf(buf, sizeof(buf), "[%d%%] %s", ctx->progress,
+		 ctx->title ? ctx->title : "Hotspot 2.0 client");
+	gtk_window_set_title(GTK_WINDOW(ctx->win), buf);
+}
+
+
+static void process_request_starting_uri(struct browser_context *ctx,
+					 const char *uri)
+{
+	int quit = 0;
+
+	if (g_str_has_prefix(uri, "osu://")) {
+		ctx->success = atoi(uri + 6);
+		quit = 1;
+	} else if (g_str_has_prefix(uri, "http://localhost:12345")) {
+		/*
+		 * This is used as a special trigger to indicate that the
+		 * user exchange has been completed.
+		 */
+		ctx->success = 1;
+		quit = 1;
+	}
+
+	if (quit) {
+		if (ctx->gtk_main_started) {
+			gtk_main_quit();
+			ctx->gtk_main_started = 0;
+		} else {
+			ctx->quit_gtk_main = 1;
+		}
+	}
+}
+
+
+#ifdef USE_WEBKIT2
+
+static void view_cb_notify_estimated_load_progress(WebKitWebView *view,
+						   GParamSpec *pspec,
+						   struct browser_context *ctx)
+{
+	ctx->progress = 100 * webkit_web_view_get_estimated_load_progress(view);
+	wpa_printf(MSG_DEBUG, "BROWSER:%s progress=%d", __func__,
+		   ctx->progress);
+	browser_update_title(ctx);
+}
+
+
+static void view_cb_resource_load_starting(WebKitWebView *view,
+					   WebKitWebResource *res,
+					   WebKitURIRequest *req,
+					   struct browser_context *ctx)
+{
+	const gchar *uri = webkit_uri_request_get_uri(req);
+
+	wpa_printf(MSG_DEBUG, "BROWSER:%s uri=%s", __func__, uri);
+	process_request_starting_uri(ctx, uri);
+}
+
+
+static gboolean view_cb_decide_policy(WebKitWebView *view,
+				      WebKitPolicyDecision *policy,
+				      WebKitPolicyDecisionType type,
+				      struct browser_context *ctx)
+{
+	wpa_printf(MSG_DEBUG, "BROWSER:%s type=%d", __func__, type);
+	switch (type) {
+	case WEBKIT_POLICY_DECISION_TYPE_RESPONSE: {
+		/* This function makes webkit send a download signal for all
+		 * unknown mime types. */
+		WebKitResponsePolicyDecision *response;
+
+		response = WEBKIT_RESPONSE_POLICY_DECISION(policy);
+		if (!webkit_response_policy_decision_is_mime_type_supported(
+			    response)) {
+			webkit_policy_decision_download(policy);
+			return TRUE;
+		}
+		break;
+	}
+	case WEBKIT_POLICY_DECISION_TYPE_NAVIGATION_ACTION: {
+		WebKitNavigationPolicyDecision *d;
+		WebKitNavigationAction *a;
+		WebKitURIRequest *req;
+		const gchar *uri;
+
+		d = WEBKIT_NAVIGATION_POLICY_DECISION(policy);
+		a = webkit_navigation_policy_decision_get_navigation_action(d);
+		req = webkit_navigation_action_get_request(a);
+		uri = webkit_uri_request_get_uri(req);
+		wpa_printf(MSG_DEBUG, "BROWSER:%s navigation action: uri=%s",
+			   __func__, uri);
+		process_request_starting_uri(ctx, uri);
+		break;
+	}
+	default:
+		break;
+	}
+
+	return FALSE;
+}
+
+
+static void view_cb_mouse_target_changed(WebKitWebView *view,
+					 WebKitHitTestResult *h,
+					 guint modifiers,
+					 struct browser_context *ctx)
+{
+	WebKitHitTestResultContext hc = webkit_hit_test_result_get_context(h);
+	const char *uri = NULL;
+
+	if (hc & WEBKIT_HIT_TEST_RESULT_CONTEXT_LINK)
+		uri = webkit_hit_test_result_get_link_uri(h);
+	else if (hc & WEBKIT_HIT_TEST_RESULT_CONTEXT_IMAGE)
+		uri = webkit_hit_test_result_get_image_uri(h);
+	else if (hc & WEBKIT_HIT_TEST_RESULT_CONTEXT_MEDIA)
+		uri = webkit_hit_test_result_get_media_uri(h);
+
+	wpa_printf(MSG_DEBUG, "BROWSER:%s uri=%s", __func__, uri ? uri : "N/A");
+	os_free(ctx->hover_link);
+	if (uri)
+		ctx->hover_link = os_strdup(uri);
+	else
+		ctx->hover_link = NULL;
+
+	browser_update_title(ctx);
+}
+
+
+static void view_cb_notify_title(WebKitWebView *view, GParamSpec *ps,
+				 struct browser_context *ctx)
+{
+	const char *title;
+
+	title = webkit_web_view_get_title(ctx->view);
+	wpa_printf(MSG_DEBUG, "BROWSER:%s title=%s", __func__, title);
+	os_free(ctx->title);
+	ctx->title = os_strdup(title);
+	browser_update_title(ctx);
+}
+
+#else /* USE_WEBKIT2 */
+
+static void view_cb_notify_progress(WebKitWebView *view, GParamSpec *pspec,
+				    struct browser_context *ctx)
+{
+	ctx->progress = 100 * webkit_web_view_get_progress(view);
+	wpa_printf(MSG_DEBUG, "BROWSER:%s progress=%d", __func__,
+		   ctx->progress);
+	browser_update_title(ctx);
+}
+
+
+static void view_cb_notify_load_status(WebKitWebView *view, GParamSpec *pspec,
+				       struct browser_context *ctx)
+{
+	int status = webkit_web_view_get_load_status(view);
+	wpa_printf(MSG_DEBUG, "BROWSER:%s load-status=%d uri=%s",
+		   __func__, status, webkit_web_view_get_uri(view));
+	if (ctx->quit_gtk_main) {
+		gtk_main_quit();
+		ctx->gtk_main_started = 0;
+	}
+}
+
+
+static void view_cb_resource_request_starting(WebKitWebView *view,
+					      WebKitWebFrame *frame,
+					      WebKitWebResource *res,
+					      WebKitNetworkRequest *req,
+					      WebKitNetworkResponse *resp,
+					      struct browser_context *ctx)
+{
+	const gchar *uri = webkit_network_request_get_uri(req);
+
+	wpa_printf(MSG_DEBUG, "BROWSER:%s uri=%s", __func__, uri);
+	if (g_str_has_suffix(uri, "/favicon.ico"))
+		webkit_network_request_set_uri(req, "about:blank");
+
+	process_request_starting_uri(ctx, uri);
+}
+
+
+static gboolean view_cb_mime_type_policy_decision(
+	WebKitWebView *view, WebKitWebFrame *frame, WebKitNetworkRequest *req,
+	gchar *mime, WebKitWebPolicyDecision *policy,
+	struct browser_context *ctx)
+{
+	wpa_printf(MSG_DEBUG, "BROWSER:%s mime=%s", __func__, mime);
+
+	if (!webkit_web_view_can_show_mime_type(view, mime)) {
+		webkit_web_policy_decision_download(policy);
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+
+static gboolean view_cb_download_requested(WebKitWebView *view,
+					   WebKitDownload *dl,
+					   struct browser_context *ctx)
+{
+	const gchar *uri;
+	uri = webkit_download_get_uri(dl);
+	wpa_printf(MSG_DEBUG, "BROWSER:%s uri=%s", __func__, uri);
+	return FALSE;
+}
+
+
+static void view_cb_hovering_over_link(WebKitWebView *view, gchar *title,
+				       gchar *uri, struct browser_context *ctx)
+{
+	wpa_printf(MSG_DEBUG, "BROWSER:%s title=%s uri=%s", __func__, title,
+		   uri);
+	os_free(ctx->hover_link);
+	if (uri)
+		ctx->hover_link = os_strdup(uri);
+	else
+		ctx->hover_link = NULL;
+
+	browser_update_title(ctx);
+}
+
+
+static void view_cb_title_changed(WebKitWebView *view, WebKitWebFrame *frame,
+				  const char *title,
+				  struct browser_context *ctx)
+{
+	wpa_printf(MSG_DEBUG, "BROWSER:%s title=%s", __func__, title);
+	os_free(ctx->title);
+	ctx->title = os_strdup(title);
+	browser_update_title(ctx);
+}
+
+#endif /* USE_WEBKIT2 */
+
+
+int hs20_web_browser(const char *url, int ignore_tls)
+{
+	GtkWidget *scroll;
+	WebKitWebView *view;
+#ifdef USE_WEBKIT2
+	WebKitSettings *settings;
+#else /* USE_WEBKIT2 */
+	WebKitWebSettings *settings;
+	SoupSession *s;
+#endif /* USE_WEBKIT2 */
+	struct browser_context ctx;
+
+	memset(&ctx, 0, sizeof(ctx));
+	if (!gtk_init_check(NULL, NULL))
+		return -1;
+
+#ifndef USE_WEBKIT2
+	s = webkit_get_default_session();
+	g_object_set(G_OBJECT(s), "ssl-ca-file",
+		     "/etc/ssl/certs/ca-certificates.crt", NULL);
+	if (ignore_tls)
+		g_object_set(G_OBJECT(s), "ssl-strict", FALSE, NULL);
+#endif /* USE_WEBKIT2 */
+
+	ctx.win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+	gtk_window_set_role(GTK_WINDOW(ctx.win), "Hotspot 2.0 client");
+	gtk_window_set_default_size(GTK_WINDOW(ctx.win), 800, 600);
+
+	scroll = gtk_scrolled_window_new(NULL, NULL);
+	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll),
+				       GTK_POLICY_NEVER, GTK_POLICY_NEVER);
+
+	g_signal_connect(G_OBJECT(ctx.win), "destroy",
+			 G_CALLBACK(win_cb_destroy), &ctx);
+
+	view = WEBKIT_WEB_VIEW(webkit_web_view_new());
+	ctx.view = view;
+#ifdef USE_WEBKIT2
+	g_signal_connect(G_OBJECT(view), "notify::estimated-load-progress",
+			 G_CALLBACK(view_cb_notify_estimated_load_progress),
+			 &ctx);
+	g_signal_connect(G_OBJECT(view), "resource-load-started",
+			 G_CALLBACK(view_cb_resource_load_starting), &ctx);
+	g_signal_connect(G_OBJECT(view), "decide-policy",
+			 G_CALLBACK(view_cb_decide_policy), &ctx);
+	g_signal_connect(G_OBJECT(view), "mouse-target-changed",
+			 G_CALLBACK(view_cb_mouse_target_changed), &ctx);
+	g_signal_connect(G_OBJECT(view), "notify::title",
+			 G_CALLBACK(view_cb_notify_title), &ctx);
+#else /* USE_WEBKIT2 */
+	g_signal_connect(G_OBJECT(view), "notify::load-status",
+			 G_CALLBACK(view_cb_notify_load_status), &ctx);
+	g_signal_connect(G_OBJECT(view), "notify::progress",
+			 G_CALLBACK(view_cb_notify_progress), &ctx);
+	g_signal_connect(G_OBJECT(view), "resource-request-starting",
+			 G_CALLBACK(view_cb_resource_request_starting), &ctx);
+	g_signal_connect(G_OBJECT(view), "mime-type-policy-decision-requested",
+			 G_CALLBACK(view_cb_mime_type_policy_decision), &ctx);
+	g_signal_connect(G_OBJECT(view), "download-requested",
+			 G_CALLBACK(view_cb_download_requested), &ctx);
+	g_signal_connect(G_OBJECT(view), "hovering-over-link",
+			 G_CALLBACK(view_cb_hovering_over_link), &ctx);
+	g_signal_connect(G_OBJECT(view), "title-changed",
+			 G_CALLBACK(view_cb_title_changed), &ctx);
+#endif /* USE_WEBKIT2 */
+
+	gtk_container_add(GTK_CONTAINER(scroll), GTK_WIDGET(view));
+	gtk_container_add(GTK_CONTAINER(ctx.win), GTK_WIDGET(scroll));
+
+	gtk_widget_grab_focus(GTK_WIDGET(view));
+	gtk_widget_show_all(ctx.win);
+
+	settings = webkit_web_view_get_settings(view);
+	g_object_set(G_OBJECT(settings), "user-agent",
+		     "Mozilla/5.0 (X11; U; Unix; en-US) "
+		     "AppleWebKit/537.15 (KHTML, like Gecko) "
+		     "hs20-client/1.0", NULL);
+	g_object_set(G_OBJECT(settings), "auto-load-images", TRUE, NULL);
+
+#ifdef USE_WEBKIT2
+	if (ignore_tls) {
+#if WEBKIT_CHECK_VERSION(2, 32, 0)
+		WebKitWebContext *wkctx;
+		WebKitWebsiteDataManager *wkmgr;
+
+		wkctx = webkit_web_context_get_default();
+		wkmgr = webkit_web_context_get_website_data_manager(wkctx);
+		webkit_website_data_manager_set_tls_errors_policy(
+			wkmgr, WEBKIT_TLS_ERRORS_POLICY_IGNORE);
+#else
+		WebKitWebContext *wkctx;
+
+		wkctx = webkit_web_context_get_default();
+		webkit_web_context_set_tls_errors_policy(
+			wkctx, WEBKIT_TLS_ERRORS_POLICY_IGNORE);
+#endif
+	}
+#endif /* USE_WEBKIT2 */
+
+	webkit_web_view_load_uri(view, url);
+
+	ctx.gtk_main_started = 1;
+	gtk_main();
+	gtk_widget_destroy(ctx.win);
+	while (gtk_events_pending())
+		gtk_main_iteration();
+
+	free(ctx.hover_link);
+	free(ctx.title);
+	return ctx.success;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/browser.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/browser.h
new file mode 100644
index 0000000..3af13b9
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/browser.h
@@ -0,0 +1,21 @@
+/*
+ * Hotspot 2.0 client - Web browser
+ * Copyright (c) 2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef BROWSER_H
+#define BROWSER_H
+
+#ifdef CONFIG_NO_BROWSER
+static inline int hs20_web_browser(const char *url, int ignore_tls)
+{
+	return -1;
+}
+#else /* CONFIG_NO_BROWSER */
+int hs20_web_browser(const char *url, int ignore_tls);
+#endif /* CONFIG_NO_BROWSER */
+
+#endif /* BROWSER_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/build_config.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/build_config.h
new file mode 100644
index 0000000..c6f4e43
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/build_config.h
@@ -0,0 +1,50 @@
+/*
+ * wpa_supplicant/hostapd - Build time configuration defines
+ * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ *
+ * This header file can be used to define configuration defines that were
+ * originally defined in Makefile. This is mainly meant for IDE use or for
+ * systems that do not have suitable 'make' tool. In these cases, it may be
+ * easier to have a single place for defining all the needed C pre-processor
+ * defines.
+ */
+
+#ifndef BUILD_CONFIG_H
+#define BUILD_CONFIG_H
+
+/* Insert configuration defines, e.g., #define EAP_MD5, here, if needed. */
+
+#ifdef CONFIG_WIN32_DEFAULTS
+#define CONFIG_NATIVE_WINDOWS
+#define CONFIG_ANSI_C_EXTRA
+#define CONFIG_WINPCAP
+#define IEEE8021X_EAPOL
+#define PKCS12_FUNCS
+#define PCSC_FUNCS
+#define CONFIG_CTRL_IFACE
+#define CONFIG_CTRL_IFACE_NAMED_PIPE
+#define CONFIG_DRIVER_NDIS
+#define CONFIG_NDIS_EVENTS_INTEGRATED
+#define CONFIG_DEBUG_FILE
+#define EAP_MD5
+#define EAP_TLS
+#define EAP_MSCHAPv2
+#define EAP_PEAP
+#define EAP_TTLS
+#define EAP_GTC
+#define EAP_OTP
+#define EAP_LEAP
+#define EAP_TNC
+#define _CRT_SECURE_NO_DEPRECATE
+
+#ifdef USE_INTERNAL_CRYPTO
+#define CONFIG_TLS_INTERNAL_CLIENT
+#define CONFIG_INTERNAL_LIBTOMMATH
+#define CONFIG_CRYPTO_INTERNAL
+#endif /* USE_INTERNAL_CRYPTO */
+#endif /* CONFIG_WIN32_DEFAULTS */
+
+#endif /* BUILD_CONFIG_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/build_features.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/build_features.h
new file mode 100644
index 0000000..553769e
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/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/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/common.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/common.c
new file mode 100644
index 0000000..6acfcbd
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/common.c
@@ -0,0 +1,1304 @@
+/*
+ * wpa_supplicant/hostapd / common helper functions, etc.
+ * Copyright (c) 2002-2019, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <limits.h>
+
+#include "common/ieee802_11_defs.h"
+#include "common.h"
+
+
+int hex2num(char c)
+{
+	if (c >= '0' && c <= '9')
+		return c - '0';
+	if (c >= 'a' && c <= 'f')
+		return c - 'a' + 10;
+	if (c >= 'A' && c <= 'F')
+		return c - 'A' + 10;
+	return -1;
+}
+
+
+int hex2byte(const char *hex)
+{
+	int a, b;
+	a = hex2num(*hex++);
+	if (a < 0)
+		return -1;
+	b = hex2num(*hex++);
+	if (b < 0)
+		return -1;
+	return (a << 4) | b;
+}
+
+
+static const char * hwaddr_parse(const char *txt, u8 *addr)
+{
+	size_t i;
+
+	for (i = 0; i < ETH_ALEN; i++) {
+		int a;
+
+		a = hex2byte(txt);
+		if (a < 0)
+			return NULL;
+		txt += 2;
+		addr[i] = a;
+		if (i < ETH_ALEN - 1 && *txt++ != ':')
+			return NULL;
+	}
+	return txt;
+}
+
+
+/**
+ * hwaddr_aton - Convert ASCII string to MAC address (colon-delimited format)
+ * @txt: MAC address as a string (e.g., "00:11:22:33:44:55")
+ * @addr: Buffer for the MAC address (ETH_ALEN = 6 bytes)
+ * Returns: 0 on success, -1 on failure (e.g., string not a MAC address)
+ */
+int hwaddr_aton(const char *txt, u8 *addr)
+{
+	return hwaddr_parse(txt, addr) ? 0 : -1;
+}
+
+
+/**
+ * hwaddr_masked_aton - Convert ASCII string with optional mask to MAC address (colon-delimited format)
+ * @txt: MAC address with optional mask as a string (e.g., "00:11:22:33:44:55/ff:ff:ff:ff:00:00")
+ * @addr: Buffer for the MAC address (ETH_ALEN = 6 bytes)
+ * @mask: Buffer for the MAC address mask (ETH_ALEN = 6 bytes)
+ * @maskable: Flag to indicate whether a mask is allowed
+ * Returns: 0 on success, -1 on failure (e.g., string not a MAC address)
+ */
+int hwaddr_masked_aton(const char *txt, u8 *addr, u8 *mask, u8 maskable)
+{
+	const char *r;
+
+	/* parse address part */
+	r = hwaddr_parse(txt, addr);
+	if (!r)
+		return -1;
+
+	/* check for optional mask */
+	if (*r == '\0' || isspace((unsigned char) *r)) {
+		/* no mask specified, assume default */
+		os_memset(mask, 0xff, ETH_ALEN);
+	} else if (maskable && *r == '/') {
+		/* mask specified and allowed */
+		r = hwaddr_parse(r + 1, mask);
+		/* parser error? */
+		if (!r)
+			return -1;
+	} else {
+		/* mask specified but not allowed or trailing garbage */
+		return -1;
+	}
+
+	return 0;
+}
+
+
+/**
+ * hwaddr_compact_aton - Convert ASCII string to MAC address (no colon delimitors format)
+ * @txt: MAC address as a string (e.g., "001122334455")
+ * @addr: Buffer for the MAC address (ETH_ALEN = 6 bytes)
+ * Returns: 0 on success, -1 on failure (e.g., string not a MAC address)
+ */
+int hwaddr_compact_aton(const char *txt, u8 *addr)
+{
+	int i;
+
+	for (i = 0; i < 6; i++) {
+		int a, b;
+
+		a = hex2num(*txt++);
+		if (a < 0)
+			return -1;
+		b = hex2num(*txt++);
+		if (b < 0)
+			return -1;
+		*addr++ = (a << 4) | b;
+	}
+
+	return 0;
+}
+
+/**
+ * hwaddr_aton2 - Convert ASCII string to MAC address (in any known format)
+ * @txt: MAC address as a string (e.g., 00:11:22:33:44:55 or 0011.2233.4455)
+ * @addr: Buffer for the MAC address (ETH_ALEN = 6 bytes)
+ * Returns: Characters used (> 0) on success, -1 on failure
+ */
+int hwaddr_aton2(const char *txt, u8 *addr)
+{
+	int i;
+	const char *pos = txt;
+
+	for (i = 0; i < 6; i++) {
+		int a, b;
+
+		while (*pos == ':' || *pos == '.' || *pos == '-')
+			pos++;
+
+		a = hex2num(*pos++);
+		if (a < 0)
+			return -1;
+		b = hex2num(*pos++);
+		if (b < 0)
+			return -1;
+		*addr++ = (a << 4) | b;
+	}
+
+	return pos - txt;
+}
+
+
+/**
+ * hexstr2bin - Convert ASCII hex string into binary data
+ * @hex: ASCII hex string (e.g., "01ab")
+ * @buf: Buffer for the binary data
+ * @len: Length of the text to convert in bytes (of buf); hex will be double
+ * this size
+ * Returns: 0 on success, -1 on failure (invalid hex string)
+ */
+int hexstr2bin(const char *hex, u8 *buf, size_t len)
+{
+	size_t i;
+	int a;
+	const char *ipos = hex;
+	u8 *opos = buf;
+
+	for (i = 0; i < len; i++) {
+		a = hex2byte(ipos);
+		if (a < 0)
+			return -1;
+		*opos++ = a;
+		ipos += 2;
+	}
+	return 0;
+}
+
+
+int hwaddr_mask_txt(char *buf, size_t len, const u8 *addr, const u8 *mask)
+{
+	size_t i;
+	int print_mask = 0;
+	int res;
+
+	for (i = 0; i < ETH_ALEN; i++) {
+		if (mask[i] != 0xff) {
+			print_mask = 1;
+			break;
+		}
+	}
+
+	if (print_mask)
+		res = os_snprintf(buf, len, MACSTR "/" MACSTR,
+				  MAC2STR(addr), MAC2STR(mask));
+	else
+		res = os_snprintf(buf, len, MACSTR, MAC2STR(addr));
+	if (os_snprintf_error(len, res))
+		return -1;
+	return res;
+}
+
+
+/**
+ * inc_byte_array - Increment arbitrary length byte array by one
+ * @counter: Pointer to byte array
+ * @len: Length of the counter in bytes
+ *
+ * This function increments the last byte of the counter by one and continues
+ * rolling over to more significant bytes if the byte was incremented from
+ * 0xff to 0x00.
+ */
+void inc_byte_array(u8 *counter, size_t len)
+{
+	int pos = len - 1;
+	while (pos >= 0) {
+		counter[pos]++;
+		if (counter[pos] != 0)
+			break;
+		pos--;
+	}
+}
+
+
+void buf_shift_right(u8 *buf, size_t len, size_t bits)
+{
+	size_t i;
+
+	for (i = len - 1; i > 0; i--)
+		buf[i] = (buf[i - 1] << (8 - bits)) | (buf[i] >> bits);
+	buf[0] >>= bits;
+}
+
+
+void wpa_get_ntp_timestamp(u8 *buf)
+{
+	struct os_time now;
+	u32 sec, usec;
+	be32 tmp;
+
+	/* 64-bit NTP timestamp (time from 1900-01-01 00:00:00) */
+	os_get_time(&now);
+	sec = now.sec + 2208988800U; /* Epoch to 1900 */
+	/* Estimate 2^32/10^6 = 4295 - 1/32 - 1/512 */
+	usec = now.usec;
+	usec = 4295 * usec - (usec >> 5) - (usec >> 9);
+	tmp = host_to_be32(sec);
+	os_memcpy(buf, (u8 *) &tmp, 4);
+	tmp = host_to_be32(usec);
+	os_memcpy(buf + 4, (u8 *) &tmp, 4);
+}
+
+/**
+ * wpa_scnprintf - Simpler-to-use snprintf function
+ * @buf: Output buffer
+ * @size: Buffer size
+ * @fmt: format
+ *
+ * Simpler snprintf version that doesn't require further error checks - the
+ * return value only indicates how many bytes were actually written, excluding
+ * the NULL byte (i.e., 0 on error, size-1 if buffer is not big enough).
+ */
+int wpa_scnprintf(char *buf, size_t size, const char *fmt, ...)
+{
+	va_list ap;
+	int ret;
+
+	if (!size)
+		return 0;
+
+	va_start(ap, fmt);
+	ret = vsnprintf(buf, size, fmt, ap);
+	va_end(ap);
+
+	if (ret < 0)
+		return 0;
+	if ((size_t) ret >= size)
+		return size - 1;
+
+	return ret;
+}
+
+
+int wpa_snprintf_hex_sep(char *buf, size_t buf_size, const u8 *data, size_t len,
+			 char sep)
+{
+	size_t i;
+	char *pos = buf, *end = buf + buf_size;
+	int ret;
+
+	if (buf_size == 0)
+		return 0;
+
+	for (i = 0; i < len; i++) {
+		ret = os_snprintf(pos, end - pos, "%02x%c",
+				  data[i], sep);
+		if (os_snprintf_error(end - pos, ret)) {
+			end[-1] = '\0';
+			return pos - buf;
+		}
+		pos += ret;
+	}
+	pos[-1] = '\0';
+	return pos - buf;
+}
+
+
+static inline int _wpa_snprintf_hex(char *buf, size_t buf_size, const u8 *data,
+				    size_t len, int uppercase)
+{
+	size_t i;
+	char *pos = buf, *end = buf + buf_size;
+	int ret;
+	if (buf_size == 0)
+		return 0;
+	for (i = 0; i < len; i++) {
+		ret = os_snprintf(pos, end - pos, uppercase ? "%02X" : "%02x",
+				  data[i]);
+		if (os_snprintf_error(end - pos, ret)) {
+			end[-1] = '\0';
+			return pos - buf;
+		}
+		pos += ret;
+	}
+	end[-1] = '\0';
+	return pos - buf;
+}
+
+/**
+ * wpa_snprintf_hex - Print data as a hex string into a buffer
+ * @buf: Memory area to use as the output buffer
+ * @buf_size: Maximum buffer size in bytes (should be at least 2 * len + 1)
+ * @data: Data to be printed
+ * @len: Length of data in bytes
+ * Returns: Number of bytes written
+ */
+int wpa_snprintf_hex(char *buf, size_t buf_size, const u8 *data, size_t len)
+{
+	return _wpa_snprintf_hex(buf, buf_size, data, len, 0);
+}
+
+
+/**
+ * wpa_snprintf_hex_uppercase - Print data as a upper case hex string into buf
+ * @buf: Memory area to use as the output buffer
+ * @buf_size: Maximum buffer size in bytes (should be at least 2 * len + 1)
+ * @data: Data to be printed
+ * @len: Length of data in bytes
+ * Returns: Number of bytes written
+ */
+int wpa_snprintf_hex_uppercase(char *buf, size_t buf_size, const u8 *data,
+			       size_t len)
+{
+	return _wpa_snprintf_hex(buf, buf_size, data, len, 1);
+}
+
+
+#ifdef CONFIG_ANSI_C_EXTRA
+
+#ifdef _WIN32_WCE
+void perror(const char *s)
+{
+	wpa_printf(MSG_ERROR, "%s: GetLastError: %d",
+		   s, (int) GetLastError());
+}
+#endif /* _WIN32_WCE */
+
+
+int optind = 1;
+int optopt;
+char *optarg;
+
+int getopt(int argc, char *const argv[], const char *optstring)
+{
+	static int optchr = 1;
+	char *cp;
+
+	if (optchr == 1) {
+		if (optind >= argc) {
+			/* all arguments processed */
+			return EOF;
+		}
+
+		if (argv[optind][0] != '-' || argv[optind][1] == '\0') {
+			/* no option characters */
+			return EOF;
+		}
+	}
+
+	if (os_strcmp(argv[optind], "--") == 0) {
+		/* no more options */
+		optind++;
+		return EOF;
+	}
+
+	optopt = argv[optind][optchr];
+	cp = os_strchr(optstring, optopt);
+	if (cp == NULL || optopt == ':') {
+		if (argv[optind][++optchr] == '\0') {
+			optchr = 1;
+			optind++;
+		}
+		return '?';
+	}
+
+	if (cp[1] == ':') {
+		/* Argument required */
+		optchr = 1;
+		if (argv[optind][optchr + 1]) {
+			/* No space between option and argument */
+			optarg = &argv[optind++][optchr + 1];
+		} else if (++optind >= argc) {
+			/* option requires an argument */
+			return '?';
+		} else {
+			/* Argument in the next argv */
+			optarg = argv[optind++];
+		}
+	} else {
+		/* No argument */
+		if (argv[optind][++optchr] == '\0') {
+			optchr = 1;
+			optind++;
+		}
+		optarg = NULL;
+	}
+	return *cp;
+}
+#endif /* CONFIG_ANSI_C_EXTRA */
+
+
+#ifdef CONFIG_NATIVE_WINDOWS
+/**
+ * wpa_unicode2ascii_inplace - Convert unicode string into ASCII
+ * @str: Pointer to string to convert
+ *
+ * This function converts a unicode string to ASCII using the same
+ * buffer for output. If UNICODE is not set, the buffer is not
+ * modified.
+ */
+void wpa_unicode2ascii_inplace(TCHAR *str)
+{
+#ifdef UNICODE
+	char *dst = (char *) str;
+	while (*str)
+		*dst++ = (char) *str++;
+	*dst = '\0';
+#endif /* UNICODE */
+}
+
+
+TCHAR * wpa_strdup_tchar(const char *str)
+{
+#ifdef UNICODE
+	TCHAR *buf;
+	buf = os_malloc((strlen(str) + 1) * sizeof(TCHAR));
+	if (buf == NULL)
+		return NULL;
+	wsprintf(buf, L"%S", str);
+	return buf;
+#else /* UNICODE */
+	return os_strdup(str);
+#endif /* UNICODE */
+}
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+
+void printf_encode(char *txt, size_t maxlen, const u8 *data, size_t len)
+{
+	char *end = txt + maxlen;
+	size_t i;
+
+	for (i = 0; i < len; i++) {
+		if (txt + 4 >= end)
+			break;
+
+		switch (data[i]) {
+		case '\"':
+			*txt++ = '\\';
+			*txt++ = '\"';
+			break;
+		case '\\':
+			*txt++ = '\\';
+			*txt++ = '\\';
+			break;
+		case '\033':
+			*txt++ = '\\';
+			*txt++ = 'e';
+			break;
+		case '\n':
+			*txt++ = '\\';
+			*txt++ = 'n';
+			break;
+		case '\r':
+			*txt++ = '\\';
+			*txt++ = 'r';
+			break;
+		case '\t':
+			*txt++ = '\\';
+			*txt++ = 't';
+			break;
+		default:
+			if (data[i] >= 32 && data[i] <= 126) {
+				*txt++ = data[i];
+			} else {
+				txt += os_snprintf(txt, end - txt, "\\x%02x",
+						   data[i]);
+			}
+			break;
+		}
+	}
+
+	*txt = '\0';
+}
+
+
+size_t printf_decode(u8 *buf, size_t maxlen, const char *str)
+{
+	const char *pos = str;
+	size_t len = 0;
+	int val;
+
+	while (*pos) {
+		if (len + 1 >= maxlen)
+			break;
+		switch (*pos) {
+		case '\\':
+			pos++;
+			switch (*pos) {
+			case '\\':
+				buf[len++] = '\\';
+				pos++;
+				break;
+			case '"':
+				buf[len++] = '"';
+				pos++;
+				break;
+			case 'n':
+				buf[len++] = '\n';
+				pos++;
+				break;
+			case 'r':
+				buf[len++] = '\r';
+				pos++;
+				break;
+			case 't':
+				buf[len++] = '\t';
+				pos++;
+				break;
+			case 'e':
+				buf[len++] = '\033';
+				pos++;
+				break;
+			case 'x':
+				pos++;
+				val = hex2byte(pos);
+				if (val < 0) {
+					val = hex2num(*pos);
+					if (val < 0)
+						break;
+					buf[len++] = val;
+					pos++;
+				} else {
+					buf[len++] = val;
+					pos += 2;
+				}
+				break;
+			case '0':
+			case '1':
+			case '2':
+			case '3':
+			case '4':
+			case '5':
+			case '6':
+			case '7':
+				val = *pos++ - '0';
+				if (*pos >= '0' && *pos <= '7')
+					val = val * 8 + (*pos++ - '0');
+				if (*pos >= '0' && *pos <= '7')
+					val = val * 8 + (*pos++ - '0');
+				buf[len++] = val;
+				break;
+			default:
+				break;
+			}
+			break;
+		default:
+			buf[len++] = *pos++;
+			break;
+		}
+	}
+	if (maxlen > len)
+		buf[len] = '\0';
+
+	return len;
+}
+
+
+/**
+ * wpa_ssid_txt - Convert SSID to a printable string
+ * @ssid: SSID (32-octet string)
+ * @ssid_len: Length of ssid in octets
+ * Returns: Pointer to a printable string
+ *
+ * This function can be used to convert SSIDs into printable form. In most
+ * cases, SSIDs do not use unprintable characters, but IEEE 802.11 standard
+ * does not limit the used character set, so anything could be used in an SSID.
+ *
+ * This function uses a static buffer, so only one call can be used at the
+ * time, i.e., this is not re-entrant and the returned buffer must be used
+ * before calling this again.
+ */
+const char * wpa_ssid_txt(const u8 *ssid, size_t ssid_len)
+{
+	static char ssid_txt[SSID_MAX_LEN * 4 + 1];
+
+	if (ssid == NULL) {
+		ssid_txt[0] = '\0';
+		return ssid_txt;
+	}
+
+	printf_encode(ssid_txt, sizeof(ssid_txt), ssid, ssid_len);
+	return ssid_txt;
+}
+
+
+void * __hide_aliasing_typecast(void *foo)
+{
+	return foo;
+}
+
+
+char * wpa_config_parse_string(const char *value, size_t *len)
+{
+	if (*value == '"') {
+		const char *pos;
+		char *str;
+		value++;
+		pos = os_strrchr(value, '"');
+		if (pos == NULL || pos[1] != '\0')
+			return NULL;
+		*len = pos - value;
+		str = dup_binstr(value, *len);
+		if (str == NULL)
+			return NULL;
+		return str;
+	} else if (*value == 'P' && value[1] == '"') {
+		const char *pos;
+		char *tstr, *str;
+		size_t tlen;
+		value += 2;
+		pos = os_strrchr(value, '"');
+		if (pos == NULL || pos[1] != '\0')
+			return NULL;
+		tlen = pos - value;
+		tstr = dup_binstr(value, tlen);
+		if (tstr == NULL)
+			return NULL;
+
+		str = os_malloc(tlen + 1);
+		if (str == NULL) {
+			os_free(tstr);
+			return NULL;
+		}
+
+		*len = printf_decode((u8 *) str, tlen + 1, tstr);
+		os_free(tstr);
+
+		return str;
+	} else {
+		u8 *str;
+		size_t tlen, hlen = os_strlen(value);
+		if (hlen & 1)
+			return NULL;
+		tlen = hlen / 2;
+		str = os_malloc(tlen + 1);
+		if (str == NULL)
+			return NULL;
+		if (hexstr2bin(value, str, tlen)) {
+			os_free(str);
+			return NULL;
+		}
+		str[tlen] = '\0';
+		*len = tlen;
+		return (char *) str;
+	}
+}
+
+
+int is_hex(const u8 *data, size_t len)
+{
+	size_t i;
+
+	for (i = 0; i < len; i++) {
+		if (data[i] < 32 || data[i] >= 127)
+			return 1;
+	}
+	return 0;
+}
+
+
+int has_ctrl_char(const u8 *data, size_t len)
+{
+	size_t i;
+
+	for (i = 0; i < len; i++) {
+		if (data[i] < 32 || data[i] == 127)
+			return 1;
+	}
+	return 0;
+}
+
+
+int has_newline(const char *str)
+{
+	while (*str) {
+		if (*str == '\n' || *str == '\r')
+			return 1;
+		str++;
+	}
+	return 0;
+}
+
+
+size_t merge_byte_arrays(u8 *res, size_t res_len,
+			 const u8 *src1, size_t src1_len,
+			 const u8 *src2, size_t src2_len)
+{
+	size_t len = 0;
+
+	os_memset(res, 0, res_len);
+
+	if (src1) {
+		if (src1_len >= res_len) {
+			os_memcpy(res, src1, res_len);
+			return res_len;
+		}
+
+		os_memcpy(res, src1, src1_len);
+		len += src1_len;
+	}
+
+	if (src2) {
+		if (len + src2_len >= res_len) {
+			os_memcpy(res + len, src2, res_len - len);
+			return res_len;
+		}
+
+		os_memcpy(res + len, src2, src2_len);
+		len += src2_len;
+	}
+
+	return len;
+}
+
+
+char * dup_binstr(const void *src, size_t len)
+{
+	char *res;
+
+	if (src == NULL)
+		return NULL;
+	res = os_malloc(len + 1);
+	if (res == NULL)
+		return NULL;
+	os_memcpy(res, src, len);
+	res[len] = '\0';
+
+	return res;
+}
+
+
+int freq_range_list_parse(struct wpa_freq_range_list *res, const char *value)
+{
+	struct wpa_freq_range *freq = NULL, *n;
+	unsigned int count = 0;
+	const char *pos, *pos2, *pos3;
+
+	/*
+	 * Comma separated list of frequency ranges.
+	 * For example: 2412-2432,2462,5000-6000
+	 */
+	pos = value;
+	while (pos && pos[0]) {
+		if (count == UINT_MAX) {
+			os_free(freq);
+			return -1;
+		}
+		n = os_realloc_array(freq, count + 1,
+				     sizeof(struct wpa_freq_range));
+		if (n == NULL) {
+			os_free(freq);
+			return -1;
+		}
+		freq = n;
+		freq[count].min = atoi(pos);
+		pos2 = os_strchr(pos, '-');
+		pos3 = os_strchr(pos, ',');
+		if (pos2 && (!pos3 || pos2 < pos3)) {
+			pos2++;
+			freq[count].max = atoi(pos2);
+		} else
+			freq[count].max = freq[count].min;
+		pos = pos3;
+		if (pos)
+			pos++;
+		count++;
+	}
+
+	os_free(res->range);
+	res->range = freq;
+	res->num = count;
+
+	return 0;
+}
+
+
+int freq_range_list_includes(const struct wpa_freq_range_list *list,
+			     unsigned int freq)
+{
+	unsigned int i;
+
+	if (list == NULL)
+		return 0;
+
+	for (i = 0; i < list->num; i++) {
+		if (freq >= list->range[i].min && freq <= list->range[i].max)
+			return 1;
+	}
+
+	return 0;
+}
+
+
+char * freq_range_list_str(const struct wpa_freq_range_list *list)
+{
+	char *buf, *pos, *end;
+	size_t maxlen;
+	unsigned int i;
+	int res;
+
+	if (list->num == 0)
+		return NULL;
+
+	maxlen = list->num * 30;
+	buf = os_malloc(maxlen);
+	if (buf == NULL)
+		return NULL;
+	pos = buf;
+	end = buf + maxlen;
+
+	for (i = 0; i < list->num; i++) {
+		struct wpa_freq_range *range = &list->range[i];
+
+		if (range->min == range->max)
+			res = os_snprintf(pos, end - pos, "%s%u",
+					  i == 0 ? "" : ",", range->min);
+		else
+			res = os_snprintf(pos, end - pos, "%s%u-%u",
+					  i == 0 ? "" : ",",
+					  range->min, range->max);
+		if (os_snprintf_error(end - pos, res)) {
+			os_free(buf);
+			return NULL;
+		}
+		pos += res;
+	}
+
+	return buf;
+}
+
+
+size_t int_array_len(const int *a)
+{
+	size_t i;
+
+	for (i = 0; a && a[i]; i++)
+		;
+	return i;
+}
+
+
+void int_array_concat(int **res, const int *a)
+{
+	size_t reslen, alen, i, max_size;
+	int *n;
+
+	reslen = int_array_len(*res);
+	alen = int_array_len(a);
+	max_size = (size_t) -1;
+	if (alen >= max_size - reslen) {
+		/* This should not really happen, but if it did, something
+		 * would overflow. Do not try to merge the arrays; instead, make
+		 * this behave like memory allocation failure to avoid messing
+		 * up memory. */
+		os_free(*res);
+		*res = NULL;
+		return;
+	}
+	n = os_realloc_array(*res, reslen + alen + 1, sizeof(int));
+	if (n == NULL) {
+		os_free(*res);
+		*res = NULL;
+		return;
+	}
+	for (i = 0; i <= alen; i++)
+		n[reslen + i] = a[i];
+	*res = n;
+}
+
+
+static int freq_cmp(const void *a, const void *b)
+{
+	int _a = *(int *) a;
+	int _b = *(int *) b;
+
+	if (_a == 0)
+		return 1;
+	if (_b == 0)
+		return -1;
+	return _a - _b;
+}
+
+
+void int_array_sort_unique(int *a)
+{
+	size_t alen, i, j;
+
+	if (a == NULL)
+		return;
+
+	alen = int_array_len(a);
+	qsort(a, alen, sizeof(int), freq_cmp);
+
+	i = 0;
+	j = 1;
+	while (a[i] && a[j]) {
+		if (a[i] == a[j]) {
+			j++;
+			continue;
+		}
+		a[++i] = a[j++];
+	}
+	if (a[i])
+		i++;
+	a[i] = 0;
+}
+
+
+void int_array_add_unique(int **res, int a)
+{
+	size_t reslen, max_size;
+	int *n;
+
+	for (reslen = 0; *res && (*res)[reslen]; reslen++) {
+		if ((*res)[reslen] == a)
+			return; /* already in the list */
+	}
+
+	max_size = (size_t) -1;
+	if (reslen > max_size - 2) {
+		/* This should not really happen in practice, but if it did,
+		 * something would overflow. Do not try to add the new value;
+		 * instead, make this behave like memory allocation failure to
+		 * avoid messing up memory. */
+		os_free(*res);
+		*res = NULL;
+		return;
+	}
+	n = os_realloc_array(*res, reslen + 2, sizeof(int));
+	if (n == NULL) {
+		os_free(*res);
+		*res = NULL;
+		return;
+	}
+
+	n[reslen] = a;
+	n[reslen + 1] = 0;
+
+	*res = n;
+}
+
+
+void str_clear_free(char *str)
+{
+	if (str) {
+		size_t len = os_strlen(str);
+		forced_memzero(str, len);
+		os_free(str);
+	}
+}
+
+
+void bin_clear_free(void *bin, size_t len)
+{
+	if (bin) {
+		forced_memzero(bin, len);
+		os_free(bin);
+	}
+}
+
+
+int random_mac_addr(u8 *addr)
+{
+	if (os_get_random(addr, ETH_ALEN) < 0)
+		return -1;
+	addr[0] &= 0xfe; /* unicast */
+	addr[0] |= 0x02; /* locally administered */
+	return 0;
+}
+
+
+int random_mac_addr_keep_oui(u8 *addr)
+{
+	if (os_get_random(addr + 3, 3) < 0)
+		return -1;
+	addr[0] &= 0xfe; /* unicast */
+	addr[0] |= 0x02; /* locally administered */
+	return 0;
+}
+
+
+/**
+ * cstr_token - Get next token from const char string
+ * @str: a constant string to tokenize
+ * @delim: a string of delimiters
+ * @last: a pointer to a character following the returned token
+ *      It has to be set to NULL for the first call and passed for any
+ *      further call.
+ * Returns: a pointer to token position in str or NULL
+ *
+ * This function is similar to str_token, but it can be used with both
+ * char and const char strings. Differences:
+ * - The str buffer remains unmodified
+ * - The returned token is not a NULL terminated string, but a token
+ *   position in str buffer. If a return value is not NULL a size
+ *   of the returned token could be calculated as (last - token).
+ */
+const char * cstr_token(const char *str, const char *delim, const char **last)
+{
+	const char *end, *token = str;
+
+	if (!str || !delim || !last)
+		return NULL;
+
+	if (*last)
+		token = *last;
+
+	while (*token && os_strchr(delim, *token))
+		token++;
+
+	if (!*token)
+		return NULL;
+
+	end = token + 1;
+
+	while (*end && !os_strchr(delim, *end))
+		end++;
+
+	*last = end;
+	return token;
+}
+
+
+/**
+ * str_token - Get next token from a string
+ * @buf: String to tokenize. Note that the string might be modified.
+ * @delim: String of delimiters
+ * @context: Pointer to save our context. Should be initialized with
+ *	NULL on the first call, and passed for any further call.
+ * Returns: The next token, NULL if there are no more valid tokens.
+ */
+char * str_token(char *str, const char *delim, char **context)
+{
+	char *token = (char *) cstr_token(str, delim, (const char **) context);
+
+	if (token && **context)
+		*(*context)++ = '\0';
+
+	return token;
+}
+
+
+size_t utf8_unescape(const char *inp, size_t in_size,
+		     char *outp, size_t out_size)
+{
+	size_t res_size = 0;
+
+	if (!inp || !outp)
+		return 0;
+
+	if (!in_size)
+		in_size = os_strlen(inp);
+
+	/* Advance past leading single quote */
+	if (*inp == '\'' && in_size) {
+		inp++;
+		in_size--;
+	}
+
+	while (in_size) {
+		in_size--;
+		if (res_size >= out_size)
+			return 0;
+
+		switch (*inp) {
+		case '\'':
+			/* Terminate on bare single quote */
+			*outp = '\0';
+			return res_size;
+
+		case '\\':
+			if (!in_size)
+				return 0;
+			in_size--;
+			inp++;
+			/* fall through */
+
+		default:
+			*outp++ = *inp++;
+			res_size++;
+		}
+	}
+
+	/* NUL terminate if space allows */
+	if (res_size < out_size)
+		*outp = '\0';
+
+	return res_size;
+}
+
+
+size_t utf8_escape(const char *inp, size_t in_size,
+		   char *outp, size_t out_size)
+{
+	size_t res_size = 0;
+
+	if (!inp || !outp)
+		return 0;
+
+	/* inp may or may not be NUL terminated, but must be if 0 size
+	 * is specified */
+	if (!in_size)
+		in_size = os_strlen(inp);
+
+	while (in_size) {
+		in_size--;
+		if (res_size++ >= out_size)
+			return 0;
+
+		switch (*inp) {
+		case '\\':
+		case '\'':
+			if (res_size++ >= out_size)
+				return 0;
+			*outp++ = '\\';
+			/* fall through */
+
+		default:
+			*outp++ = *inp++;
+			break;
+		}
+	}
+
+	/* NUL terminate if space allows */
+	if (res_size < out_size)
+		*outp = '\0';
+
+	return res_size;
+}
+
+
+int is_ctrl_char(char c)
+{
+	return c > 0 && c < 32;
+}
+
+
+/**
+ * ssid_parse - Parse a string that contains SSID in hex or text format
+ * @buf: Input NULL terminated string that contains the SSID
+ * @ssid: Output SSID
+ * Returns: 0 on success, -1 otherwise
+ *
+ * The SSID has to be enclosed in double quotes for the text format or space
+ * or NULL terminated string of hex digits for the hex format. buf can include
+ * additional arguments after the SSID.
+ */
+int ssid_parse(const char *buf, struct wpa_ssid_value *ssid)
+{
+	char *tmp, *res, *end;
+	size_t len;
+
+	ssid->ssid_len = 0;
+
+	tmp = os_strdup(buf);
+	if (!tmp)
+		return -1;
+
+	if (*tmp != '"') {
+		end = os_strchr(tmp, ' ');
+		if (end)
+			*end = '\0';
+	} else {
+		end = os_strchr(tmp + 1, '"');
+		if (!end) {
+			os_free(tmp);
+			return -1;
+		}
+
+		end[1] = '\0';
+	}
+
+	res = wpa_config_parse_string(tmp, &len);
+	if (res && len <= SSID_MAX_LEN) {
+		ssid->ssid_len = len;
+		os_memcpy(ssid->ssid, res, len);
+	}
+
+	os_free(tmp);
+	os_free(res);
+
+	return ssid->ssid_len ? 0 : -1;
+}
+
+
+int str_starts(const char *str, const char *start)
+{
+	return os_strncmp(str, start, os_strlen(start)) == 0;
+}
+
+
+/**
+ * rssi_to_rcpi - Convert RSSI to RCPI
+ * @rssi: RSSI to convert
+ * Returns: RCPI corresponding to the given RSSI value, or 255 if not available.
+ *
+ * It's possible to estimate RCPI based on RSSI in dBm. This calculation will
+ * not reflect the correct value for high rates, but it's good enough for Action
+ * frames which are transmitted with up to 24 Mbps rates.
+ */
+u8 rssi_to_rcpi(int rssi)
+{
+	if (!rssi)
+		return 255; /* not available */
+	if (rssi < -110)
+		return 0;
+	if (rssi > 0)
+		return 220;
+	return (rssi + 110) * 2;
+}
+
+
+char * get_param(const char *cmd, const char *param)
+{
+	const char *pos, *end;
+	char *val;
+	size_t len;
+
+	pos = os_strstr(cmd, param);
+	if (!pos)
+		return NULL;
+
+	pos += os_strlen(param);
+	end = os_strchr(pos, ' ');
+	if (end)
+		len = end - pos;
+	else
+		len = os_strlen(pos);
+	val = os_malloc(len + 1);
+	if (!val)
+		return NULL;
+	os_memcpy(val, pos, len);
+	val[len] = '\0';
+	return val;
+}
+
+
+/* Try to prevent most compilers from optimizing out clearing of memory that
+ * becomes unaccessible after this function is called. This is mostly the case
+ * for clearing local stack variables at the end of a function. This is not
+ * exactly perfect, i.e., someone could come up with a compiler that figures out
+ * the pointer is pointing to memset and then end up optimizing the call out, so
+ * try go a bit further by storing the first octet (now zero) to make this even
+ * a bit more difficult to optimize out. Once memset_s() is available, that
+ * could be used here instead. */
+static void * (* const volatile memset_func)(void *, int, size_t) = memset;
+static u8 forced_memzero_val;
+
+void forced_memzero(void *ptr, size_t len)
+{
+	memset_func(ptr, 0, len);
+	if (len)
+		forced_memzero_val = ((u8 *) ptr)[0];
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/common.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/common.h
new file mode 100644
index 0000000..435a9a8
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/common.h
@@ -0,0 +1,599 @@
+/*
+ * wpa_supplicant/hostapd / common helper functions, etc.
+ * Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef COMMON_H
+#define COMMON_H
+
+#include "os.h"
+
+#if defined(__linux__) || defined(__GLIBC__)
+#include <endian.h>
+#include <byteswap.h>
+#endif /* __linux__ */
+
+#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__DragonFly__) || \
+    defined(__OpenBSD__)
+#include <sys/types.h>
+#include <sys/endian.h>
+#define __BYTE_ORDER	_BYTE_ORDER
+#define	__LITTLE_ENDIAN	_LITTLE_ENDIAN
+#define	__BIG_ENDIAN	_BIG_ENDIAN
+#ifdef __OpenBSD__
+#define bswap_16 swap16
+#define bswap_32 swap32
+#define bswap_64 swap64
+#else /* __OpenBSD__ */
+#define bswap_16 bswap16
+#define bswap_32 bswap32
+#define bswap_64 bswap64
+#endif /* __OpenBSD__ */
+#endif /* defined(__FreeBSD__) || defined(__NetBSD__) ||
+	* defined(__DragonFly__) || defined(__OpenBSD__) */
+
+#ifdef __APPLE__
+#include <sys/types.h>
+#include <machine/endian.h>
+#define __BYTE_ORDER	_BYTE_ORDER
+#define __LITTLE_ENDIAN	_LITTLE_ENDIAN
+#define __BIG_ENDIAN	_BIG_ENDIAN
+static inline unsigned short bswap_16(unsigned short v)
+{
+	return ((v & 0xff) << 8) | (v >> 8);
+}
+
+static inline unsigned int bswap_32(unsigned int v)
+{
+	return ((v & 0xff) << 24) | ((v & 0xff00) << 8) |
+		((v & 0xff0000) >> 8) | (v >> 24);
+}
+#endif /* __APPLE__ */
+
+#ifdef __rtems__
+#include <rtems/endian.h>
+#define __BYTE_ORDER BYTE_ORDER
+#define __LITTLE_ENDIAN LITTLE_ENDIAN
+#define __BIG_ENDIAN BIG_ENDIAN
+#define bswap_16 CPU_swap_u16
+#define bswap_32 CPU_swap_u32
+#endif /* __rtems__ */
+
+#ifdef CONFIG_NATIVE_WINDOWS
+#include <winsock.h>
+
+typedef int socklen_t;
+
+#ifndef MSG_DONTWAIT
+#define MSG_DONTWAIT 0 /* not supported */
+#endif
+
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+#ifdef _MSC_VER
+#define inline __inline
+
+#undef vsnprintf
+#define vsnprintf _vsnprintf
+#undef close
+#define close closesocket
+#endif /* _MSC_VER */
+
+
+/* Define platform specific integer types */
+
+#ifdef _MSC_VER
+typedef UINT64 u64;
+typedef UINT32 u32;
+typedef UINT16 u16;
+typedef UINT8 u8;
+typedef INT64 s64;
+typedef INT32 s32;
+typedef INT16 s16;
+typedef INT8 s8;
+#define WPA_TYPES_DEFINED
+#endif /* _MSC_VER */
+
+#ifdef __vxworks
+typedef unsigned long long u64;
+typedef UINT32 u32;
+typedef UINT16 u16;
+typedef UINT8 u8;
+typedef long long s64;
+typedef INT32 s32;
+typedef INT16 s16;
+typedef INT8 s8;
+#define WPA_TYPES_DEFINED
+#endif /* __vxworks */
+
+#ifndef WPA_TYPES_DEFINED
+#ifdef CONFIG_USE_INTTYPES_H
+#include <inttypes.h>
+#else
+#include <stdint.h>
+#endif
+typedef uint64_t u64;
+typedef uint32_t u32;
+typedef uint16_t u16;
+typedef uint8_t u8;
+typedef int64_t s64;
+typedef int32_t s32;
+typedef int16_t s16;
+typedef int8_t s8;
+#define WPA_TYPES_DEFINED
+#endif /* !WPA_TYPES_DEFINED */
+
+
+/* Define platform specific byte swapping macros */
+
+#if defined(__CYGWIN__) || defined(CONFIG_NATIVE_WINDOWS)
+
+static inline unsigned short wpa_swap_16(unsigned short v)
+{
+	return ((v & 0xff) << 8) | (v >> 8);
+}
+
+static inline unsigned int wpa_swap_32(unsigned int v)
+{
+	return ((v & 0xff) << 24) | ((v & 0xff00) << 8) |
+		((v & 0xff0000) >> 8) | (v >> 24);
+}
+
+#define le_to_host16(n) (n)
+#define host_to_le16(n) (n)
+#define be_to_host16(n) wpa_swap_16(n)
+#define host_to_be16(n) wpa_swap_16(n)
+#define le_to_host32(n) (n)
+#define host_to_le32(n) (n)
+#define be_to_host32(n) wpa_swap_32(n)
+#define host_to_be32(n) wpa_swap_32(n)
+#define host_to_le64(n) (n)
+
+#define WPA_BYTE_SWAP_DEFINED
+
+#endif /* __CYGWIN__ || CONFIG_NATIVE_WINDOWS */
+
+
+#ifndef WPA_BYTE_SWAP_DEFINED
+
+#ifndef __BYTE_ORDER
+#ifndef __LITTLE_ENDIAN
+#ifndef __BIG_ENDIAN
+#define __LITTLE_ENDIAN 1234
+#define __BIG_ENDIAN 4321
+#if defined(sparc)
+#define __BYTE_ORDER __BIG_ENDIAN
+#endif
+#endif /* __BIG_ENDIAN */
+#endif /* __LITTLE_ENDIAN */
+#endif /* __BYTE_ORDER */
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define le_to_host16(n) ((__force u16) (le16) (n))
+#define host_to_le16(n) ((__force le16) (u16) (n))
+#define be_to_host16(n) bswap_16((__force u16) (be16) (n))
+#define host_to_be16(n) ((__force be16) bswap_16((n)))
+#define le_to_host32(n) ((__force u32) (le32) (n))
+#define host_to_le32(n) ((__force le32) (u32) (n))
+#define be_to_host32(n) bswap_32((__force u32) (be32) (n))
+#define host_to_be32(n) ((__force be32) bswap_32((n)))
+#define le_to_host64(n) ((__force u64) (le64) (n))
+#define host_to_le64(n) ((__force le64) (u64) (n))
+#define be_to_host64(n) bswap_64((__force u64) (be64) (n))
+#define host_to_be64(n) ((__force be64) bswap_64((n)))
+#elif __BYTE_ORDER == __BIG_ENDIAN
+#define le_to_host16(n) bswap_16(n)
+#define host_to_le16(n) bswap_16(n)
+#define be_to_host16(n) (n)
+#define host_to_be16(n) (n)
+#define le_to_host32(n) bswap_32(n)
+#define host_to_le32(n) bswap_32(n)
+#define be_to_host32(n) (n)
+#define host_to_be32(n) (n)
+#define le_to_host64(n) bswap_64(n)
+#define host_to_le64(n) bswap_64(n)
+#define be_to_host64(n) (n)
+#define host_to_be64(n) (n)
+#ifndef WORDS_BIGENDIAN
+#define WORDS_BIGENDIAN
+#endif
+#else
+#error Could not determine CPU byte order
+#endif
+
+#define WPA_BYTE_SWAP_DEFINED
+#endif /* !WPA_BYTE_SWAP_DEFINED */
+
+
+/* Macros for handling unaligned memory accesses */
+
+static inline u16 WPA_GET_BE16(const u8 *a)
+{
+	return (a[0] << 8) | a[1];
+}
+
+static inline void WPA_PUT_BE16(u8 *a, u16 val)
+{
+	a[0] = val >> 8;
+	a[1] = val & 0xff;
+}
+
+static inline u16 WPA_GET_LE16(const u8 *a)
+{
+	return (a[1] << 8) | a[0];
+}
+
+static inline void WPA_PUT_LE16(u8 *a, u16 val)
+{
+	a[1] = val >> 8;
+	a[0] = val & 0xff;
+}
+
+static inline u32 WPA_GET_BE24(const u8 *a)
+{
+	return (a[0] << 16) | (a[1] << 8) | a[2];
+}
+
+static inline void WPA_PUT_BE24(u8 *a, u32 val)
+{
+	a[0] = (val >> 16) & 0xff;
+	a[1] = (val >> 8) & 0xff;
+	a[2] = val & 0xff;
+}
+
+static inline u32 WPA_GET_BE32(const u8 *a)
+{
+	return ((u32) a[0] << 24) | (a[1] << 16) | (a[2] << 8) | a[3];
+}
+
+static inline void WPA_PUT_BE32(u8 *a, u32 val)
+{
+	a[0] = (val >> 24) & 0xff;
+	a[1] = (val >> 16) & 0xff;
+	a[2] = (val >> 8) & 0xff;
+	a[3] = val & 0xff;
+}
+
+static inline u32 WPA_GET_LE32(const u8 *a)
+{
+	return ((u32) a[3] << 24) | (a[2] << 16) | (a[1] << 8) | a[0];
+}
+
+static inline void WPA_PUT_LE32(u8 *a, u32 val)
+{
+	a[3] = (val >> 24) & 0xff;
+	a[2] = (val >> 16) & 0xff;
+	a[1] = (val >> 8) & 0xff;
+	a[0] = val & 0xff;
+}
+
+static inline u64 WPA_GET_BE64(const u8 *a)
+{
+	return (((u64) a[0]) << 56) | (((u64) a[1]) << 48) |
+		(((u64) a[2]) << 40) | (((u64) a[3]) << 32) |
+		(((u64) a[4]) << 24) | (((u64) a[5]) << 16) |
+		(((u64) a[6]) << 8) | ((u64) a[7]);
+}
+
+static inline void WPA_PUT_BE64(u8 *a, u64 val)
+{
+	a[0] = val >> 56;
+	a[1] = val >> 48;
+	a[2] = val >> 40;
+	a[3] = val >> 32;
+	a[4] = val >> 24;
+	a[5] = val >> 16;
+	a[6] = val >> 8;
+	a[7] = val & 0xff;
+}
+
+static inline u64 WPA_GET_LE64(const u8 *a)
+{
+	return (((u64) a[7]) << 56) | (((u64) a[6]) << 48) |
+		(((u64) a[5]) << 40) | (((u64) a[4]) << 32) |
+		(((u64) a[3]) << 24) | (((u64) a[2]) << 16) |
+		(((u64) a[1]) << 8) | ((u64) a[0]);
+}
+
+static inline void WPA_PUT_LE64(u8 *a, u64 val)
+{
+	a[7] = val >> 56;
+	a[6] = val >> 48;
+	a[5] = val >> 40;
+	a[4] = val >> 32;
+	a[3] = val >> 24;
+	a[2] = val >> 16;
+	a[1] = val >> 8;
+	a[0] = val & 0xff;
+}
+
+
+#ifndef ETH_ALEN
+#define ETH_ALEN 6
+#endif
+#ifndef ETH_HLEN
+#define ETH_HLEN 14
+#endif
+#ifndef IFNAMSIZ
+#define IFNAMSIZ 16
+#endif
+#ifndef ETH_P_ALL
+#define ETH_P_ALL 0x0003
+#endif
+#ifndef ETH_P_IP
+#define ETH_P_IP 0x0800
+#endif
+#ifndef ETH_P_80211_ENCAP
+#define ETH_P_80211_ENCAP 0x890d /* TDLS comes under this category */
+#endif
+#ifndef ETH_P_PAE
+#define ETH_P_PAE 0x888E /* Port Access Entity (IEEE 802.1X) */
+#endif /* ETH_P_PAE */
+#ifndef ETH_P_EAPOL
+#define ETH_P_EAPOL ETH_P_PAE
+#endif /* ETH_P_EAPOL */
+#ifndef ETH_P_RSN_PREAUTH
+#define ETH_P_RSN_PREAUTH 0x88c7
+#endif /* ETH_P_RSN_PREAUTH */
+#ifndef ETH_P_RRB
+#define ETH_P_RRB 0x890D
+#endif /* ETH_P_RRB */
+#ifndef ETH_P_OUI
+#define ETH_P_OUI 0x88B7
+#endif /* ETH_P_OUI */
+#ifndef ETH_P_8021Q
+#define ETH_P_8021Q 0x8100
+#endif /* ETH_P_8021Q */
+
+
+#ifdef __GNUC__
+#define PRINTF_FORMAT(a,b) __attribute__ ((format (printf, (a), (b))))
+#define STRUCT_PACKED __attribute__ ((packed))
+#else
+#define PRINTF_FORMAT(a,b)
+#define STRUCT_PACKED
+#endif
+
+
+#ifdef CONFIG_ANSI_C_EXTRA
+
+#if !defined(_MSC_VER) || _MSC_VER < 1400
+/* snprintf - used in number of places; sprintf() is _not_ a good replacement
+ * due to possible buffer overflow; see, e.g.,
+ * http://www.ijs.si/software/snprintf/ for portable implementation of
+ * snprintf. */
+int snprintf(char *str, size_t size, const char *format, ...);
+
+/* vsnprintf - only used for wpa_msg() in wpa_supplicant.c */
+int vsnprintf(char *str, size_t size, const char *format, va_list ap);
+#endif /* !defined(_MSC_VER) || _MSC_VER < 1400 */
+
+/* getopt - only used in main.c */
+int getopt(int argc, char *const argv[], const char *optstring);
+extern char *optarg;
+extern int optind;
+
+#ifndef CONFIG_NO_SOCKLEN_T_TYPEDEF
+#ifndef __socklen_t_defined
+typedef int socklen_t;
+#endif
+#endif
+
+/* inline - define as __inline or just define it to be empty, if needed */
+#ifdef CONFIG_NO_INLINE
+#define inline
+#else
+#define inline __inline
+#endif
+
+#ifndef __func__
+#define __func__ "__func__ not defined"
+#endif
+
+#ifndef bswap_16
+#define bswap_16(a) ((((u16) (a) << 8) & 0xff00) | (((u16) (a) >> 8) & 0xff))
+#endif
+
+#ifndef bswap_32
+#define bswap_32(a) ((((u32) (a) << 24) & 0xff000000) | \
+		     (((u32) (a) << 8) & 0xff0000) | \
+     		     (((u32) (a) >> 8) & 0xff00) | \
+     		     (((u32) (a) >> 24) & 0xff))
+#endif
+
+#ifndef MSG_DONTWAIT
+#define MSG_DONTWAIT 0
+#endif
+
+#ifdef _WIN32_WCE
+void perror(const char *s);
+#endif /* _WIN32_WCE */
+
+#endif /* CONFIG_ANSI_C_EXTRA */
+
+#ifndef MAC2STR
+#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5]
+#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x"
+
+/*
+ * Compact form for string representation of MAC address
+ * To be used, e.g., for constructing dbus paths for P2P Devices
+ */
+#define COMPACT_MACSTR "%02x%02x%02x%02x%02x%02x"
+#endif
+
+#ifndef BIT
+#define BIT(x) (1U << (x))
+#endif
+
+/*
+ * Definitions for sparse validation
+ * (http://kernel.org/pub/linux/kernel/people/josh/sparse/)
+ */
+#ifdef __CHECKER__
+#define __force __attribute__((force))
+#undef __bitwise
+#define __bitwise __attribute__((bitwise))
+#else
+#define __force
+#undef __bitwise
+#define __bitwise
+#endif
+
+typedef u16 __bitwise be16;
+typedef u16 __bitwise le16;
+typedef u32 __bitwise be32;
+typedef u32 __bitwise le32;
+typedef u64 __bitwise be64;
+typedef u64 __bitwise le64;
+
+#ifndef __must_check
+#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
+#define __must_check __attribute__((__warn_unused_result__))
+#else
+#define __must_check
+#endif /* __GNUC__ */
+#endif /* __must_check */
+
+#ifndef __maybe_unused
+#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
+#define __maybe_unused __attribute__((unused))
+#else
+#define __maybe_unused
+#endif /* __GNUC__ */
+#endif /* __must_check */
+
+#define SSID_MAX_LEN 32
+
+struct wpa_ssid_value {
+	u8 ssid[SSID_MAX_LEN];
+	size_t ssid_len;
+};
+
+int hwaddr_aton(const char *txt, u8 *addr);
+int hwaddr_masked_aton(const char *txt, u8 *addr, u8 *mask, u8 maskable);
+int hwaddr_compact_aton(const char *txt, u8 *addr);
+int hwaddr_aton2(const char *txt, u8 *addr);
+int hex2num(char c);
+int hex2byte(const char *hex);
+int hexstr2bin(const char *hex, u8 *buf, size_t len);
+void inc_byte_array(u8 *counter, size_t len);
+void buf_shift_right(u8 *buf, size_t len, size_t bits);
+void wpa_get_ntp_timestamp(u8 *buf);
+int wpa_scnprintf(char *buf, size_t size, const char *fmt, ...)
+	PRINTF_FORMAT(3, 4);
+int wpa_snprintf_hex_sep(char *buf, size_t buf_size, const u8 *data, size_t len,
+			 char sep);
+int wpa_snprintf_hex(char *buf, size_t buf_size, const u8 *data, size_t len);
+int wpa_snprintf_hex_uppercase(char *buf, size_t buf_size, const u8 *data,
+			       size_t len);
+
+int hwaddr_mask_txt(char *buf, size_t len, const u8 *addr, const u8 *mask);
+int ssid_parse(const char *buf, struct wpa_ssid_value *ssid);
+
+#ifdef CONFIG_NATIVE_WINDOWS
+void wpa_unicode2ascii_inplace(TCHAR *str);
+TCHAR * wpa_strdup_tchar(const char *str);
+#else /* CONFIG_NATIVE_WINDOWS */
+#define wpa_unicode2ascii_inplace(s) do { } while (0)
+#define wpa_strdup_tchar(s) strdup((s))
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+void printf_encode(char *txt, size_t maxlen, const u8 *data, size_t len);
+size_t printf_decode(u8 *buf, size_t maxlen, const char *str);
+
+const char * wpa_ssid_txt(const u8 *ssid, size_t ssid_len);
+
+char * wpa_config_parse_string(const char *value, size_t *len);
+int is_hex(const u8 *data, size_t len);
+int has_ctrl_char(const u8 *data, size_t len);
+int has_newline(const char *str);
+size_t merge_byte_arrays(u8 *res, size_t res_len,
+			 const u8 *src1, size_t src1_len,
+			 const u8 *src2, size_t src2_len);
+char * dup_binstr(const void *src, size_t len);
+
+static inline int is_zero_ether_addr(const u8 *a)
+{
+	return !(a[0] | a[1] | a[2] | a[3] | a[4] | a[5]);
+}
+
+static inline int is_broadcast_ether_addr(const u8 *a)
+{
+	return (a[0] & a[1] & a[2] & a[3] & a[4] & a[5]) == 0xff;
+}
+
+static inline int is_multicast_ether_addr(const u8 *a)
+{
+	return a[0] & 0x01;
+}
+
+#define broadcast_ether_addr (const u8 *) "\xff\xff\xff\xff\xff\xff"
+
+#include "wpa_debug.h"
+
+
+struct wpa_freq_range_list {
+	struct wpa_freq_range {
+		unsigned int min;
+		unsigned int max;
+	} *range;
+	unsigned int num;
+};
+
+int freq_range_list_parse(struct wpa_freq_range_list *res, const char *value);
+int freq_range_list_includes(const struct wpa_freq_range_list *list,
+			     unsigned int freq);
+char * freq_range_list_str(const struct wpa_freq_range_list *list);
+
+size_t int_array_len(const int *a);
+void int_array_concat(int **res, const int *a);
+void int_array_sort_unique(int *a);
+void int_array_add_unique(int **res, int a);
+
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
+
+void str_clear_free(char *str);
+void bin_clear_free(void *bin, size_t len);
+
+int random_mac_addr(u8 *addr);
+int random_mac_addr_keep_oui(u8 *addr);
+
+const char * cstr_token(const char *str, const char *delim, const char **last);
+char * str_token(char *str, const char *delim, char **context);
+size_t utf8_escape(const char *inp, size_t in_size,
+		   char *outp, size_t out_size);
+size_t utf8_unescape(const char *inp, size_t in_size,
+		     char *outp, size_t out_size);
+int is_ctrl_char(char c);
+
+int str_starts(const char *str, const char *start);
+
+u8 rssi_to_rcpi(int rssi);
+char * get_param(const char *cmd, const char *param);
+
+void forced_memzero(void *ptr, size_t len);
+
+/*
+ * gcc 4.4 ends up generating strict-aliasing warnings about some very common
+ * networking socket uses that do not really result in a real problem and
+ * cannot be easily avoided with union-based type-punning due to struct
+ * definitions including another struct in system header files. To avoid having
+ * to fully disable strict-aliasing warnings, provide a mechanism to hide the
+ * typecast from aliasing for now. A cleaner solution will hopefully be found
+ * in the future to handle these cases.
+ */
+void * __hide_aliasing_typecast(void *foo);
+#define aliasing_hide_typecast(a,t) (t *) __hide_aliasing_typecast((a))
+
+#ifdef CONFIG_VALGRIND
+#include <valgrind/memcheck.h>
+#define WPA_MEM_DEFINED(ptr, len) VALGRIND_MAKE_MEM_DEFINED((ptr), (len))
+#else /* CONFIG_VALGRIND */
+#define WPA_MEM_DEFINED(ptr, len) do { } while (0)
+#endif /* CONFIG_VALGRIND */
+
+#endif /* COMMON_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/config.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/config.c
new file mode 100644
index 0000000..ba26c2c
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/config.c
@@ -0,0 +1,105 @@
+/*
+ * Configuration parsing
+ * Copyright (c) 2003-2019, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "utils/config.h"
+#include "common.h"
+
+
+static int newline_terminated(const char *buf, size_t buflen)
+{
+	size_t len = os_strlen(buf);
+	if (len == 0)
+		return 0;
+	if (len == buflen - 1 && buf[buflen - 1] != '\r' &&
+	    buf[len - 1] != '\n')
+		return 0;
+	return 1;
+}
+
+
+static void skip_line_end(FILE *stream)
+{
+	char buf[100];
+	while (fgets(buf, sizeof(buf), stream)) {
+		buf[sizeof(buf) - 1] = '\0';
+		if (newline_terminated(buf, sizeof(buf)))
+			return;
+	}
+}
+
+
+char * wpa_config_get_line(char *s, int size, FILE *stream, int *line,
+			   char **_pos)
+{
+	char *pos, *end, *sstart;
+
+	while (fgets(s, size, stream)) {
+		(*line)++;
+		s[size - 1] = '\0';
+		if (!newline_terminated(s, size)) {
+			/*
+			 * The line was truncated - skip rest of it to avoid
+			 * confusing error messages.
+			 */
+			wpa_printf(MSG_INFO, "Long line in configuration file "
+				   "truncated");
+			skip_line_end(stream);
+		}
+		pos = s;
+
+		/* Skip white space from the beginning of line. */
+		while (*pos == ' ' || *pos == '\t' || *pos == '\r')
+			pos++;
+
+		/* Skip comment lines and empty lines */
+		if (*pos == '#' || *pos == '\n' || *pos == '\0')
+			continue;
+
+		/*
+		 * Remove # comments unless they are within a double quoted
+		 * string.
+		 */
+		sstart = pos;
+		end = os_strchr(sstart, '#');
+		while (end) {
+			sstart = os_strchr(sstart, '"');
+			if (!sstart || sstart > end)
+				break;
+			sstart = os_strchr(sstart + 1, '"');
+			if (!sstart)
+				break;
+			sstart++;
+			if (sstart > end)
+				end = os_strchr(sstart, '#');
+		}
+
+		if (end)
+			*end-- = '\0';
+		else
+			end = pos + os_strlen(pos) - 1;
+
+		/* Remove trailing white space. */
+		while (end > pos &&
+		       (*end == '\n' || *end == ' ' || *end == '\t' ||
+			*end == '\r'))
+			*end-- = '\0';
+
+		if (*pos == '\0')
+			continue;
+
+		if (_pos)
+			*_pos = pos;
+		return pos;
+	}
+
+	if (_pos)
+		*_pos = NULL;
+	return NULL;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/config.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/config.h
new file mode 100644
index 0000000..074a88a
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/config.h
@@ -0,0 +1,29 @@
+/*
+ * Configuration parsing
+ * Copyright (c) 2003-2019, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef UTILS_CONFIG_H
+#define UTILS_CONFIG_H
+
+/**
+ * wpa_config_get_line - Read the next configuration file line
+ * @s: Buffer for the line
+ * @size: The buffer length
+ * @stream: File stream to read from
+ * @line: Pointer to a variable storing the file line number
+ * @_pos: Buffer for the pointer to the beginning of data on the text line or
+ * %NULL if not needed (returned value used instead)
+ * Returns: Pointer to the beginning of data on the text line or %NULL if no
+ * more text lines are available.
+ *
+ * This function reads the next non-empty line from the configuration file and
+ * removes comments. The returned string is guaranteed to be null-terminated.
+ */
+char * wpa_config_get_line(char *s, int size, FILE *stream, int *line,
+			   char **_pos);
+
+#endif /* UTILS_CONFIG_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/const_time.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/const_time.h
new file mode 100644
index 0000000..ab8f611
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/const_time.h
@@ -0,0 +1,191 @@
+/*
+ * Helper functions for constant time operations
+ * Copyright (c) 2019, The Linux Foundation
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ *
+ * These helper functions can be used to implement logic that needs to minimize
+ * externally visible differences in execution path by avoiding use of branches,
+ * avoiding early termination or other time differences, and forcing same memory
+ * access pattern regardless of values.
+ */
+
+#ifndef CONST_TIME_H
+#define CONST_TIME_H
+
+
+#if defined(__clang__)
+#define NO_UBSAN_UINT_OVERFLOW \
+	__attribute__((no_sanitize("unsigned-integer-overflow")))
+#else
+#define NO_UBSAN_UINT_OVERFLOW
+#endif
+
+
+/**
+ * const_time_fill_msb - Fill all bits with MSB value
+ * @val: Input value
+ * Returns: Value with all the bits set to the MSB of the input val
+ */
+static inline unsigned int const_time_fill_msb(unsigned int val)
+{
+	/* Move the MSB to LSB and multiple by -1 to fill in all bits. */
+	return (val >> (sizeof(val) * 8 - 1)) * ~0U;
+}
+
+
+/* Returns: -1 if val is zero; 0 if val is not zero */
+static inline unsigned int const_time_is_zero(unsigned int val)
+	NO_UBSAN_UINT_OVERFLOW
+{
+	/* Set MSB to 1 for 0 and fill rest of bits with the MSB value */
+	return const_time_fill_msb(~val & (val - 1));
+}
+
+
+/* Returns: -1 if a == b; 0 if a != b */
+static inline unsigned int const_time_eq(unsigned int a, unsigned int b)
+{
+	return const_time_is_zero(a ^ b);
+}
+
+
+/* Returns: -1 if a == b; 0 if a != b */
+static inline u8 const_time_eq_u8(unsigned int a, unsigned int b)
+{
+	return (u8) const_time_eq(a, b);
+}
+
+
+/**
+ * const_time_eq_bin - Constant time memory comparison
+ * @a: First buffer to compare
+ * @b: Second buffer to compare
+ * @len: Number of octets to compare
+ * Returns: -1 if buffers are equal, 0 if not
+ *
+ * This function is meant for comparing passwords or hash values where
+ * difference in execution time or memory access pattern could provide external
+ * observer information about the location of the difference in the memory
+ * buffers. The return value does not behave like memcmp(), i.e.,
+ * const_time_eq_bin() cannot be used to sort items into a defined order. Unlike
+ * memcmp(), the execution time of const_time_eq_bin() does not depend on the
+ * contents of the compared memory buffers, but only on the total compared
+ * length.
+ */
+static inline unsigned int const_time_eq_bin(const void *a, const void *b,
+					     size_t len)
+{
+	const u8 *aa = a;
+	const u8 *bb = b;
+	size_t i;
+	u8 res = 0;
+
+	for (i = 0; i < len; i++)
+		res |= aa[i] ^ bb[i];
+
+	return const_time_is_zero(res);
+}
+
+
+/**
+ * const_time_select - Constant time unsigned int selection
+ * @mask: 0 (false) or -1 (true) to identify which value to select
+ * @true_val: Value to select for the true case
+ * @false_val: Value to select for the false case
+ * Returns: true_val if mask == -1, false_val if mask == 0
+ */
+static inline unsigned int const_time_select(unsigned int mask,
+					     unsigned int true_val,
+					     unsigned int false_val)
+{
+	return (mask & true_val) | (~mask & false_val);
+}
+
+
+/**
+ * const_time_select_int - Constant time int selection
+ * @mask: 0 (false) or -1 (true) to identify which value to select
+ * @true_val: Value to select for the true case
+ * @false_val: Value to select for the false case
+ * Returns: true_val if mask == -1, false_val if mask == 0
+ */
+static inline int const_time_select_int(unsigned int mask, int true_val,
+					int false_val)
+{
+	return (int) const_time_select(mask, (unsigned int) true_val,
+				       (unsigned int) false_val);
+}
+
+
+/**
+ * const_time_select_u8 - Constant time u8 selection
+ * @mask: 0 (false) or -1 (true) to identify which value to select
+ * @true_val: Value to select for the true case
+ * @false_val: Value to select for the false case
+ * Returns: true_val if mask == -1, false_val if mask == 0
+ */
+static inline u8 const_time_select_u8(u8 mask, u8 true_val, u8 false_val)
+{
+	return (u8) const_time_select(mask, true_val, false_val);
+}
+
+
+/**
+ * const_time_select_s8 - Constant time s8 selection
+ * @mask: 0 (false) or -1 (true) to identify which value to select
+ * @true_val: Value to select for the true case
+ * @false_val: Value to select for the false case
+ * Returns: true_val if mask == -1, false_val if mask == 0
+ */
+static inline s8 const_time_select_s8(u8 mask, s8 true_val, s8 false_val)
+{
+	return (s8) const_time_select(mask, (unsigned int) true_val,
+				      (unsigned int) false_val);
+}
+
+
+/**
+ * const_time_select_bin - Constant time binary buffer selection copy
+ * @mask: 0 (false) or -1 (true) to identify which value to copy
+ * @true_val: Buffer to copy for the true case
+ * @false_val: Buffer to copy for the false case
+ * @len: Number of octets to copy
+ * @dst: Destination buffer for the copy
+ *
+ * This function copies the specified buffer into the destination buffer using
+ * operations with identical memory access pattern regardless of which buffer
+ * is being copied.
+ */
+static inline void const_time_select_bin(u8 mask, const u8 *true_val,
+					 const u8 *false_val, size_t len,
+					 u8 *dst)
+{
+	size_t i;
+
+	for (i = 0; i < len; i++)
+		dst[i] = const_time_select_u8(mask, true_val[i], false_val[i]);
+}
+
+
+static inline int const_time_memcmp(const void *a, const void *b, size_t len)
+{
+	const u8 *aa = a;
+	const u8 *bb = b;
+	int diff, res = 0;
+	unsigned int mask;
+
+	if (len == 0)
+		return 0;
+	do {
+		len--;
+		diff = (int) aa[len] - (int) bb[len];
+		mask = const_time_is_zero((unsigned int) diff);
+		res = const_time_select_int(mask, res, diff);
+	} while (len);
+
+	return res;
+}
+
+#endif /* CONST_TIME_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/crc32.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/crc32.c
new file mode 100644
index 0000000..3712549
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/crc32.c
@@ -0,0 +1,85 @@
+/*
+ * 32-bit CRC for FCS calculation
+ * Copyright (c) 2010, Jouni Malinen <j@w1.fi>
+ *
+ * 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/crc32.h"
+
+/*
+ * IEEE 802.11 FCS CRC32
+ * G(x) = x^32 + x^26 + x^23 + x^22 + x^16 + x^12 + x^11 + x^10 + x^8 + x^7 +
+ *        x^5 + x^4 + x^2 + x + 1
+ */
+static const u32 crc32_table[256] = {
+	0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419,
+	0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4,
+	0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07,
+	0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
+	0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856,
+	0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
+	0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4,
+	0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
+	0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3,
+	0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a,
+	0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599,
+	0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
+	0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190,
+	0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f,
+	0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e,
+	0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
+	0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed,
+	0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
+	0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3,
+	0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
+	0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a,
+	0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5,
+	0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010,
+	0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+	0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17,
+	0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6,
+	0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615,
+	0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
+	0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344,
+	0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
+	0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a,
+	0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
+	0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1,
+	0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c,
+	0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef,
+	0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
+	0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe,
+	0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31,
+	0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c,
+	0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
+	0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b,
+	0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
+	0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1,
+	0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
+	0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278,
+	0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7,
+	0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66,
+	0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+	0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605,
+	0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8,
+	0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b,
+	0x2d02ef8d
+};
+
+
+u32 ieee80211_crc32(const u8 *frame, size_t frame_len)
+{
+	size_t i;
+	u32 crc;
+
+	crc = 0xFFFFFFFF;
+	for (i = 0; i < frame_len; i++)
+		crc = crc32_table[(crc ^ frame[i]) & 0xff] ^ (crc >> 8);
+
+	return ~crc;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/crc32.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/crc32.h
new file mode 100644
index 0000000..71a19dc
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/crc32.h
@@ -0,0 +1,14 @@
+/*
+ * 32-bit CRC for FCS calculation
+ * Copyright (c) 2010, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef CRC32_H
+#define CRC32_H
+
+u32 ieee80211_crc32(const u8 *frame, size_t frame_len);
+
+#endif /* CRC32_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/edit.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/edit.c
new file mode 100644
index 0000000..d340bfa
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/edit.c
@@ -0,0 +1,1174 @@
+/*
+ * Command line editing and history
+ * Copyright (c) 2010-2011, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <termios.h>
+
+#include "common.h"
+#include "eloop.h"
+#include "list.h"
+#include "edit.h"
+
+#define CMD_BUF_LEN 4096
+static char cmdbuf[CMD_BUF_LEN];
+static int cmdbuf_pos = 0;
+static int cmdbuf_len = 0;
+static char currbuf[CMD_BUF_LEN];
+static int currbuf_valid = 0;
+static const char *ps2 = NULL;
+
+#define HISTORY_MAX 100
+
+struct edit_history {
+	struct dl_list list;
+	char str[1];
+};
+
+static struct dl_list history_list;
+static struct edit_history *history_curr;
+
+static void *edit_cb_ctx;
+static void (*edit_cmd_cb)(void *ctx, char *cmd);
+static void (*edit_eof_cb)(void *ctx);
+static char ** (*edit_completion_cb)(void *ctx, const char *cmd, int pos) =
+	NULL;
+
+static struct termios prevt, newt;
+
+
+#define CLEAR_END_LINE "\e[K"
+
+
+void edit_clear_line(void)
+{
+	int i;
+	putchar('\r');
+	for (i = 0; i < cmdbuf_len + 2 + (ps2 ? (int) os_strlen(ps2) : 0); i++)
+		putchar(' ');
+}
+
+
+static void move_start(void)
+{
+	cmdbuf_pos = 0;
+	edit_redraw();
+}
+
+
+static void move_end(void)
+{
+	cmdbuf_pos = cmdbuf_len;
+	edit_redraw();
+}
+
+
+static void move_left(void)
+{
+	if (cmdbuf_pos > 0) {
+		cmdbuf_pos--;
+		edit_redraw();
+	}
+}
+
+
+static void move_right(void)
+{
+	if (cmdbuf_pos < cmdbuf_len) {
+		cmdbuf_pos++;
+		edit_redraw();
+	}
+}
+
+
+static void move_word_left(void)
+{
+	while (cmdbuf_pos > 0 && cmdbuf[cmdbuf_pos - 1] == ' ')
+		cmdbuf_pos--;
+	while (cmdbuf_pos > 0 && cmdbuf[cmdbuf_pos - 1] != ' ')
+		cmdbuf_pos--;
+	edit_redraw();
+}
+
+
+static void move_word_right(void)
+{
+	while (cmdbuf_pos < cmdbuf_len && cmdbuf[cmdbuf_pos] == ' ')
+		cmdbuf_pos++;
+	while (cmdbuf_pos < cmdbuf_len && cmdbuf[cmdbuf_pos] != ' ')
+		cmdbuf_pos++;
+	edit_redraw();
+}
+
+
+static void delete_left(void)
+{
+	if (cmdbuf_pos == 0)
+		return;
+
+	edit_clear_line();
+	os_memmove(cmdbuf + cmdbuf_pos - 1, cmdbuf + cmdbuf_pos,
+		   cmdbuf_len - cmdbuf_pos);
+	cmdbuf_pos--;
+	cmdbuf_len--;
+	edit_redraw();
+}
+
+
+static void delete_current(void)
+{
+	if (cmdbuf_pos == cmdbuf_len)
+		return;
+
+	edit_clear_line();
+	os_memmove(cmdbuf + cmdbuf_pos, cmdbuf + cmdbuf_pos + 1,
+		   cmdbuf_len - cmdbuf_pos);
+	cmdbuf_len--;
+	edit_redraw();
+}
+
+
+static void delete_word(void)
+{
+	int pos;
+
+	edit_clear_line();
+	pos = cmdbuf_pos;
+	while (pos > 0 && cmdbuf[pos - 1] == ' ')
+		pos--;
+	while (pos > 0 && cmdbuf[pos - 1] != ' ')
+		pos--;
+	os_memmove(cmdbuf + pos, cmdbuf + cmdbuf_pos, cmdbuf_len - cmdbuf_pos);
+	cmdbuf_len -= cmdbuf_pos - pos;
+	cmdbuf_pos = pos;
+	edit_redraw();
+}
+
+
+static void clear_left(void)
+{
+	if (cmdbuf_pos == 0)
+		return;
+
+	edit_clear_line();
+	os_memmove(cmdbuf, cmdbuf + cmdbuf_pos, cmdbuf_len - cmdbuf_pos);
+	cmdbuf_len -= cmdbuf_pos;
+	cmdbuf_pos = 0;
+	edit_redraw();
+}
+
+
+static void clear_right(void)
+{
+	if (cmdbuf_pos == cmdbuf_len)
+		return;
+
+	edit_clear_line();
+	cmdbuf_len = cmdbuf_pos;
+	edit_redraw();
+}
+
+
+static void history_add(const char *str)
+{
+	struct edit_history *h, *match = NULL, *last = NULL;
+	size_t len, count = 0;
+
+	if (str[0] == '\0')
+		return;
+
+	dl_list_for_each(h, &history_list, struct edit_history, list) {
+		if (os_strcmp(str, h->str) == 0) {
+			match = h;
+			break;
+		}
+		last = h;
+		count++;
+	}
+
+	if (match) {
+		dl_list_del(&h->list);
+		dl_list_add(&history_list, &h->list);
+		history_curr = h;
+		return;
+	}
+
+	if (count >= HISTORY_MAX && last) {
+		dl_list_del(&last->list);
+		os_free(last);
+	}
+
+	len = os_strlen(str);
+	h = os_zalloc(sizeof(*h) + len);
+	if (h == NULL)
+		return;
+	dl_list_add(&history_list, &h->list);
+	os_strlcpy(h->str, str, len + 1);
+	history_curr = h;
+}
+
+
+static void history_use(void)
+{
+	edit_clear_line();
+	cmdbuf_len = cmdbuf_pos = os_strlen(history_curr->str);
+	os_memcpy(cmdbuf, history_curr->str, cmdbuf_len);
+	edit_redraw();
+}
+
+
+static void history_prev(void)
+{
+	if (history_curr == NULL)
+		return;
+
+	if (history_curr ==
+	    dl_list_first(&history_list, struct edit_history, list)) {
+		if (!currbuf_valid) {
+			cmdbuf[cmdbuf_len] = '\0';
+			os_memcpy(currbuf, cmdbuf, cmdbuf_len + 1);
+			currbuf_valid = 1;
+			history_use();
+			return;
+		}
+	}
+
+	if (history_curr ==
+	    dl_list_last(&history_list, struct edit_history, list))
+		return;
+
+	history_curr = dl_list_entry(history_curr->list.next,
+				     struct edit_history, list);
+	history_use();
+}
+
+
+static void history_next(void)
+{
+	if (history_curr == NULL ||
+	    history_curr ==
+	    dl_list_first(&history_list, struct edit_history, list)) {
+		if (currbuf_valid) {
+			currbuf_valid = 0;
+			edit_clear_line();
+			cmdbuf_len = cmdbuf_pos = os_strlen(currbuf);
+			os_memcpy(cmdbuf, currbuf, cmdbuf_len);
+			edit_redraw();
+		}
+		return;
+	}
+
+	history_curr = dl_list_entry(history_curr->list.prev,
+				     struct edit_history, list);
+	history_use();
+}
+
+
+static void history_read(const char *fname)
+{
+	FILE *f;
+	char buf[CMD_BUF_LEN], *pos;
+
+	f = fopen(fname, "r");
+	if (f == NULL)
+		return;
+
+	while (fgets(buf, CMD_BUF_LEN, f)) {
+		for (pos = buf; *pos; pos++) {
+			if (*pos == '\r' || *pos == '\n') {
+				*pos = '\0';
+				break;
+			}
+		}
+		history_add(buf);
+	}
+
+	fclose(f);
+}
+
+
+static void history_write(const char *fname,
+			  int (*filter_cb)(void *ctx, const char *cmd))
+{
+	FILE *f;
+	struct edit_history *h;
+
+	f = fopen(fname, "w");
+	if (f == NULL)
+		return;
+
+	dl_list_for_each_reverse(h, &history_list, struct edit_history, list) {
+		if (filter_cb && filter_cb(edit_cb_ctx, h->str))
+			continue;
+		fprintf(f, "%s\n", h->str);
+	}
+
+	fclose(f);
+}
+
+
+static void history_debug_dump(void)
+{
+	struct edit_history *h;
+	edit_clear_line();
+	printf("\r");
+	dl_list_for_each_reverse(h, &history_list, struct edit_history, list)
+		printf("%s%s\n", h == history_curr ? "[C]" : "", h->str);
+	if (currbuf_valid)
+		printf("{%s}\n", currbuf);
+	edit_redraw();
+}
+
+
+static void insert_char(int c)
+{
+	if (cmdbuf_len >= (int) sizeof(cmdbuf) - 1)
+		return;
+	if (cmdbuf_len == cmdbuf_pos) {
+		cmdbuf[cmdbuf_pos++] = c;
+		cmdbuf_len++;
+		putchar(c);
+		fflush(stdout);
+	} else {
+		os_memmove(cmdbuf + cmdbuf_pos + 1, cmdbuf + cmdbuf_pos,
+			   cmdbuf_len - cmdbuf_pos);
+		cmdbuf[cmdbuf_pos++] = c;
+		cmdbuf_len++;
+		edit_redraw();
+	}
+}
+
+
+static void process_cmd(void)
+{
+	currbuf_valid = 0;
+	if (cmdbuf_len == 0) {
+		printf("\n%s> ", ps2 ? ps2 : "");
+		fflush(stdout);
+		return;
+	}
+	printf("\n");
+	cmdbuf[cmdbuf_len] = '\0';
+	history_add(cmdbuf);
+	cmdbuf_pos = 0;
+	cmdbuf_len = 0;
+	edit_cmd_cb(edit_cb_ctx, cmdbuf);
+	printf("%s> ", ps2 ? ps2 : "");
+	fflush(stdout);
+}
+
+
+static void free_completions(char **c)
+{
+	int i;
+	if (c == NULL)
+		return;
+	for (i = 0; c[i]; i++)
+		os_free(c[i]);
+	os_free(c);
+}
+
+
+static int filter_strings(char **c, char *str, size_t len)
+{
+	int i, j;
+
+	for (i = 0, j = 0; c[j]; j++) {
+		if (os_strncasecmp(c[j], str, len) == 0) {
+			if (i != j) {
+				c[i] = c[j];
+				c[j] = NULL;
+			}
+			i++;
+		} else {
+			os_free(c[j]);
+			c[j] = NULL;
+		}
+	}
+	c[i] = NULL;
+	return i;
+}
+
+
+static int common_len(const char *a, const char *b)
+{
+	int len = 0;
+	while (a[len] && a[len] == b[len])
+		len++;
+	return len;
+}
+
+
+static int max_common_length(char **c)
+{
+	int len, i;
+
+	len = os_strlen(c[0]);
+	for (i = 1; c[i]; i++) {
+		int same = common_len(c[0], c[i]);
+		if (same < len)
+			len = same;
+	}
+
+	return len;
+}
+
+
+static int cmp_str(const void *a, const void *b)
+{
+	return os_strcmp(* (const char **) a, * (const char **) b);
+}
+
+static void complete(int list)
+{
+	char **c;
+	int i, len, count;
+	int start, end;
+	int room, plen, add_space;
+
+	if (edit_completion_cb == NULL)
+		return;
+
+	cmdbuf[cmdbuf_len] = '\0';
+	c = edit_completion_cb(edit_cb_ctx, cmdbuf, cmdbuf_pos);
+	if (c == NULL)
+		return;
+
+	end = cmdbuf_pos;
+	start = end;
+	while (start > 0 && cmdbuf[start - 1] != ' ')
+		start--;
+	plen = end - start;
+
+	count = filter_strings(c, &cmdbuf[start], plen);
+	if (count == 0) {
+		free_completions(c);
+		return;
+	}
+
+	len = max_common_length(c);
+	if (len <= plen && count > 1) {
+		if (list) {
+			qsort(c, count, sizeof(char *), cmp_str);
+			edit_clear_line();
+			printf("\r");
+			for (i = 0; c[i]; i++)
+				printf("%s%s", i > 0 ? " " : "", c[i]);
+			printf("\n");
+			edit_redraw();
+		}
+		free_completions(c);
+		return;
+	}
+	len -= plen;
+
+	room = sizeof(cmdbuf) - 1 - cmdbuf_len;
+	if (room < len)
+		len = room;
+	add_space = count == 1 && len < room;
+
+	os_memmove(cmdbuf + cmdbuf_pos + len + add_space, cmdbuf + cmdbuf_pos,
+		   cmdbuf_len - cmdbuf_pos);
+	os_memcpy(&cmdbuf[cmdbuf_pos - plen], c[0], plen + len);
+	if (add_space)
+		cmdbuf[cmdbuf_pos + len] = ' ';
+
+	cmdbuf_pos += len + add_space;
+	cmdbuf_len += len + add_space;
+
+	edit_redraw();
+
+	free_completions(c);
+}
+
+
+enum edit_key_code {
+	EDIT_KEY_NONE = 256,
+	EDIT_KEY_TAB,
+	EDIT_KEY_UP,
+	EDIT_KEY_DOWN,
+	EDIT_KEY_RIGHT,
+	EDIT_KEY_LEFT,
+	EDIT_KEY_ENTER,
+	EDIT_KEY_BACKSPACE,
+	EDIT_KEY_INSERT,
+	EDIT_KEY_DELETE,
+	EDIT_KEY_HOME,
+	EDIT_KEY_END,
+	EDIT_KEY_PAGE_UP,
+	EDIT_KEY_PAGE_DOWN,
+	EDIT_KEY_F1,
+	EDIT_KEY_F2,
+	EDIT_KEY_F3,
+	EDIT_KEY_F4,
+	EDIT_KEY_F5,
+	EDIT_KEY_F6,
+	EDIT_KEY_F7,
+	EDIT_KEY_F8,
+	EDIT_KEY_F9,
+	EDIT_KEY_F10,
+	EDIT_KEY_F11,
+	EDIT_KEY_F12,
+	EDIT_KEY_CTRL_UP,
+	EDIT_KEY_CTRL_DOWN,
+	EDIT_KEY_CTRL_RIGHT,
+	EDIT_KEY_CTRL_LEFT,
+	EDIT_KEY_CTRL_A,
+	EDIT_KEY_CTRL_B,
+	EDIT_KEY_CTRL_D,
+	EDIT_KEY_CTRL_E,
+	EDIT_KEY_CTRL_F,
+	EDIT_KEY_CTRL_G,
+	EDIT_KEY_CTRL_H,
+	EDIT_KEY_CTRL_J,
+	EDIT_KEY_CTRL_K,
+	EDIT_KEY_CTRL_L,
+	EDIT_KEY_CTRL_N,
+	EDIT_KEY_CTRL_O,
+	EDIT_KEY_CTRL_P,
+	EDIT_KEY_CTRL_R,
+	EDIT_KEY_CTRL_T,
+	EDIT_KEY_CTRL_U,
+	EDIT_KEY_CTRL_V,
+	EDIT_KEY_CTRL_W,
+	EDIT_KEY_ALT_UP,
+	EDIT_KEY_ALT_DOWN,
+	EDIT_KEY_ALT_RIGHT,
+	EDIT_KEY_ALT_LEFT,
+	EDIT_KEY_SHIFT_UP,
+	EDIT_KEY_SHIFT_DOWN,
+	EDIT_KEY_SHIFT_RIGHT,
+	EDIT_KEY_SHIFT_LEFT,
+	EDIT_KEY_ALT_SHIFT_UP,
+	EDIT_KEY_ALT_SHIFT_DOWN,
+	EDIT_KEY_ALT_SHIFT_RIGHT,
+	EDIT_KEY_ALT_SHIFT_LEFT,
+	EDIT_KEY_EOF
+};
+
+static void show_esc_buf(const char *esc_buf, char c, int i)
+{
+	edit_clear_line();
+	printf("\rESC buffer '%s' c='%c' [%d]\n", esc_buf, c, i);
+	edit_redraw();
+}
+
+
+static enum edit_key_code esc_seq_to_key1_no(char last)
+{
+	switch (last) {
+	case 'A':
+		return EDIT_KEY_UP;
+	case 'B':
+		return EDIT_KEY_DOWN;
+	case 'C':
+		return EDIT_KEY_RIGHT;
+	case 'D':
+		return EDIT_KEY_LEFT;
+	default:
+		return EDIT_KEY_NONE;
+	}
+}
+
+
+static enum edit_key_code esc_seq_to_key1_shift(char last)
+{
+	switch (last) {
+	case 'A':
+		return EDIT_KEY_SHIFT_UP;
+	case 'B':
+		return EDIT_KEY_SHIFT_DOWN;
+	case 'C':
+		return EDIT_KEY_SHIFT_RIGHT;
+	case 'D':
+		return EDIT_KEY_SHIFT_LEFT;
+	default:
+		return EDIT_KEY_NONE;
+	}
+}
+
+
+static enum edit_key_code esc_seq_to_key1_alt(char last)
+{
+	switch (last) {
+	case 'A':
+		return EDIT_KEY_ALT_UP;
+	case 'B':
+		return EDIT_KEY_ALT_DOWN;
+	case 'C':
+		return EDIT_KEY_ALT_RIGHT;
+	case 'D':
+		return EDIT_KEY_ALT_LEFT;
+	default:
+		return EDIT_KEY_NONE;
+	}
+}
+
+
+static enum edit_key_code esc_seq_to_key1_alt_shift(char last)
+{
+	switch (last) {
+	case 'A':
+		return EDIT_KEY_ALT_SHIFT_UP;
+	case 'B':
+		return EDIT_KEY_ALT_SHIFT_DOWN;
+	case 'C':
+		return EDIT_KEY_ALT_SHIFT_RIGHT;
+	case 'D':
+		return EDIT_KEY_ALT_SHIFT_LEFT;
+	default:
+		return EDIT_KEY_NONE;
+	}
+}
+
+
+static enum edit_key_code esc_seq_to_key1_ctrl(char last)
+{
+	switch (last) {
+	case 'A':
+		return EDIT_KEY_CTRL_UP;
+	case 'B':
+		return EDIT_KEY_CTRL_DOWN;
+	case 'C':
+		return EDIT_KEY_CTRL_RIGHT;
+	case 'D':
+		return EDIT_KEY_CTRL_LEFT;
+	default:
+		return EDIT_KEY_NONE;
+	}
+}
+
+
+static enum edit_key_code esc_seq_to_key1(int param1, int param2, char last)
+{
+	/* ESC-[<param1>;<param2><last> */
+
+	if (param1 < 0 && param2 < 0)
+		return esc_seq_to_key1_no(last);
+
+	if (param1 == 1 && param2 == 2)
+		return esc_seq_to_key1_shift(last);
+
+	if (param1 == 1 && param2 == 3)
+		return esc_seq_to_key1_alt(last);
+
+	if (param1 == 1 && param2 == 4)
+		return esc_seq_to_key1_alt_shift(last);
+
+	if (param1 == 1 && param2 == 5)
+		return esc_seq_to_key1_ctrl(last);
+
+	if (param2 < 0) {
+		if (last != '~')
+			return EDIT_KEY_NONE;
+		switch (param1) {
+		case 2:
+			return EDIT_KEY_INSERT;
+		case 3:
+			return EDIT_KEY_DELETE;
+		case 5:
+			return EDIT_KEY_PAGE_UP;
+		case 6:
+			return EDIT_KEY_PAGE_DOWN;
+		case 15:
+			return EDIT_KEY_F5;
+		case 17:
+			return EDIT_KEY_F6;
+		case 18:
+			return EDIT_KEY_F7;
+		case 19:
+			return EDIT_KEY_F8;
+		case 20:
+			return EDIT_KEY_F9;
+		case 21:
+			return EDIT_KEY_F10;
+		case 23:
+			return EDIT_KEY_F11;
+		case 24:
+			return EDIT_KEY_F12;
+		}
+	}
+
+	return EDIT_KEY_NONE;
+}
+
+
+static enum edit_key_code esc_seq_to_key2(int param1, int param2, char last)
+{
+	/* ESC-O<param1>;<param2><last> */
+
+	if (param1 >= 0 || param2 >= 0)
+		return EDIT_KEY_NONE;
+
+	switch (last) {
+	case 'F':
+		return EDIT_KEY_END;
+	case 'H':
+		return EDIT_KEY_HOME;
+	case 'P':
+		return EDIT_KEY_F1;
+	case 'Q':
+		return EDIT_KEY_F2;
+	case 'R':
+		return EDIT_KEY_F3;
+	case 'S':
+		return EDIT_KEY_F4;
+	default:
+		return EDIT_KEY_NONE;
+	}
+}
+
+
+static enum edit_key_code esc_seq_to_key(char *seq)
+{
+	char last, *pos;
+	int param1 = -1, param2 = -1;
+	enum edit_key_code ret = EDIT_KEY_NONE;
+
+	last = '\0';
+	for (pos = seq; *pos; pos++)
+		last = *pos;
+
+	if (seq[1] >= '0' && seq[1] <= '9') {
+		param1 = atoi(&seq[1]);
+		pos = os_strchr(seq, ';');
+		if (pos)
+			param2 = atoi(pos + 1);
+	}
+
+	if (seq[0] == '[')
+		ret = esc_seq_to_key1(param1, param2, last);
+	else if (seq[0] == 'O')
+		ret = esc_seq_to_key2(param1, param2, last);
+
+	if (ret != EDIT_KEY_NONE)
+		return ret;
+
+	edit_clear_line();
+	printf("\rUnknown escape sequence '%s'\n", seq);
+	edit_redraw();
+	return EDIT_KEY_NONE;
+}
+
+
+static enum edit_key_code edit_read_key(int sock)
+{
+	int c;
+	unsigned char buf[1];
+	int res;
+	static int esc = -1;
+	static char esc_buf[7];
+
+	res = read(sock, buf, 1);
+	if (res < 0)
+		perror("read");
+	if (res <= 0)
+		return EDIT_KEY_EOF;
+
+	c = buf[0];
+
+	if (esc >= 0) {
+		if (c == 27 /* ESC */) {
+			esc = 0;
+			return EDIT_KEY_NONE;
+		}
+
+		if (esc == 6) {
+			show_esc_buf(esc_buf, c, 0);
+			esc = -1;
+		} else {
+			esc_buf[esc++] = c;
+			esc_buf[esc] = '\0';
+		}
+	}
+
+	if (esc == 1) {
+		if (esc_buf[0] != '[' && esc_buf[0] != 'O') {
+			show_esc_buf(esc_buf, c, 1);
+			esc = -1;
+			return EDIT_KEY_NONE;
+		} else
+			return EDIT_KEY_NONE; /* Escape sequence continues */
+	}
+
+	if (esc > 1) {
+		if ((c >= '0' && c <= '9') || c == ';')
+			return EDIT_KEY_NONE; /* Escape sequence continues */
+
+		if (c == '~' || (c >= 'A' && c <= 'Z')) {
+			esc = -1;
+			return esc_seq_to_key(esc_buf);
+		}
+
+		show_esc_buf(esc_buf, c, 2);
+		esc = -1;
+		return EDIT_KEY_NONE;
+	}
+
+	switch (c) {
+	case 1:
+		return EDIT_KEY_CTRL_A;
+	case 2:
+		return EDIT_KEY_CTRL_B;
+	case 4:
+		return EDIT_KEY_CTRL_D;
+	case 5:
+		return EDIT_KEY_CTRL_E;
+	case 6:
+		return EDIT_KEY_CTRL_F;
+	case 7:
+		return EDIT_KEY_CTRL_G;
+	case 8:
+		return EDIT_KEY_CTRL_H;
+	case 9:
+		return EDIT_KEY_TAB;
+	case 10:
+		return EDIT_KEY_CTRL_J;
+	case 13: /* CR */
+		return EDIT_KEY_ENTER;
+	case 11:
+		return EDIT_KEY_CTRL_K;
+	case 12:
+		return EDIT_KEY_CTRL_L;
+	case 14:
+		return EDIT_KEY_CTRL_N;
+	case 15:
+		return EDIT_KEY_CTRL_O;
+	case 16:
+		return EDIT_KEY_CTRL_P;
+	case 18:
+		return EDIT_KEY_CTRL_R;
+	case 20:
+		return EDIT_KEY_CTRL_T;
+	case 21:
+		return EDIT_KEY_CTRL_U;
+	case 22:
+		return EDIT_KEY_CTRL_V;
+	case 23:
+		return EDIT_KEY_CTRL_W;
+	case 27: /* ESC */
+		esc = 0;
+		return EDIT_KEY_NONE;
+	case 127:
+		return EDIT_KEY_BACKSPACE;
+	default:
+		return c;
+	}
+}
+
+
+static char search_buf[21];
+static int search_skip;
+
+static char * search_find(void)
+{
+	struct edit_history *h;
+	size_t len = os_strlen(search_buf);
+	int skip = search_skip;
+
+	if (len == 0)
+		return NULL;
+
+	dl_list_for_each(h, &history_list, struct edit_history, list) {
+		if (os_strstr(h->str, search_buf)) {
+			if (skip == 0)
+				return h->str;
+			skip--;
+		}
+	}
+
+	search_skip = 0;
+	return NULL;
+}
+
+
+static void search_redraw(void)
+{
+	char *match = search_find();
+	printf("\rsearch '%s': %s" CLEAR_END_LINE,
+	       search_buf, match ? match : "");
+	printf("\rsearch '%s", search_buf);
+	fflush(stdout);
+}
+
+
+static void search_start(void)
+{
+	edit_clear_line();
+	search_buf[0] = '\0';
+	search_skip = 0;
+	search_redraw();
+}
+
+
+static void search_clear(void)
+{
+	search_redraw();
+	printf("\r" CLEAR_END_LINE);
+}
+
+
+static void search_stop(void)
+{
+	char *match = search_find();
+	search_buf[0] = '\0';
+	search_clear();
+	if (match) {
+		os_strlcpy(cmdbuf, match, CMD_BUF_LEN);
+		cmdbuf_len = os_strlen(cmdbuf);
+		cmdbuf_pos = cmdbuf_len;
+	}
+	edit_redraw();
+}
+
+
+static void search_cancel(void)
+{
+	search_buf[0] = '\0';
+	search_clear();
+	edit_redraw();
+}
+
+
+static void search_backspace(void)
+{
+	size_t len;
+	len = os_strlen(search_buf);
+	if (len == 0)
+		return;
+	search_buf[len - 1] = '\0';
+	search_skip = 0;
+	search_redraw();
+}
+
+
+static void search_next(void)
+{
+	search_skip++;
+	search_find();
+	search_redraw();
+}
+
+
+static void search_char(char c)
+{
+	size_t len;
+	len = os_strlen(search_buf);
+	if (len == sizeof(search_buf) - 1)
+		return;
+	search_buf[len] = c;
+	search_buf[len + 1] = '\0';
+	search_skip = 0;
+	search_redraw();
+}
+
+
+static enum edit_key_code search_key(enum edit_key_code c)
+{
+	switch (c) {
+	case EDIT_KEY_ENTER:
+	case EDIT_KEY_CTRL_J:
+	case EDIT_KEY_LEFT:
+	case EDIT_KEY_RIGHT:
+	case EDIT_KEY_HOME:
+	case EDIT_KEY_END:
+	case EDIT_KEY_CTRL_A:
+	case EDIT_KEY_CTRL_E:
+		search_stop();
+		return c;
+	case EDIT_KEY_DOWN:
+	case EDIT_KEY_UP:
+		search_cancel();
+		return EDIT_KEY_EOF;
+	case EDIT_KEY_CTRL_H:
+	case EDIT_KEY_BACKSPACE:
+		search_backspace();
+		break;
+	case EDIT_KEY_CTRL_R:
+		search_next();
+		break;
+	default:
+		if (c >= 32 && c <= 255)
+			search_char(c);
+		break;
+	}
+
+	return EDIT_KEY_NONE;
+}
+
+
+static void edit_read_char(int sock, void *eloop_ctx, void *sock_ctx)
+{
+	static int last_tab = 0;
+	static int search = 0;
+	enum edit_key_code c;
+
+	c = edit_read_key(sock);
+
+	if (search) {
+		c = search_key(c);
+		if (c == EDIT_KEY_NONE)
+			return;
+		search = 0;
+		if (c == EDIT_KEY_EOF)
+			return;
+	}
+
+	if (c != EDIT_KEY_TAB && c != EDIT_KEY_NONE)
+		last_tab = 0;
+
+	switch (c) {
+	case EDIT_KEY_NONE:
+		break;
+	case EDIT_KEY_EOF:
+		edit_eof_cb(edit_cb_ctx);
+		break;
+	case EDIT_KEY_TAB:
+		complete(last_tab);
+		last_tab = 1;
+		break;
+	case EDIT_KEY_UP:
+	case EDIT_KEY_CTRL_P:
+		history_prev();
+		break;
+	case EDIT_KEY_DOWN:
+	case EDIT_KEY_CTRL_N:
+		history_next();
+		break;
+	case EDIT_KEY_RIGHT:
+	case EDIT_KEY_CTRL_F:
+		move_right();
+		break;
+	case EDIT_KEY_LEFT:
+	case EDIT_KEY_CTRL_B:
+		move_left();
+		break;
+	case EDIT_KEY_CTRL_RIGHT:
+		move_word_right();
+		break;
+	case EDIT_KEY_CTRL_LEFT:
+		move_word_left();
+		break;
+	case EDIT_KEY_DELETE:
+		delete_current();
+		break;
+	case EDIT_KEY_END:
+		move_end();
+		break;
+	case EDIT_KEY_HOME:
+	case EDIT_KEY_CTRL_A:
+		move_start();
+		break;
+	case EDIT_KEY_F2:
+		history_debug_dump();
+		break;
+	case EDIT_KEY_CTRL_D:
+		if (cmdbuf_len > 0) {
+			delete_current();
+			return;
+		}
+		printf("\n");
+		edit_eof_cb(edit_cb_ctx);
+		break;
+	case EDIT_KEY_CTRL_E:
+		move_end();
+		break;
+	case EDIT_KEY_CTRL_H:
+	case EDIT_KEY_BACKSPACE:
+		delete_left();
+		break;
+	case EDIT_KEY_ENTER:
+	case EDIT_KEY_CTRL_J:
+		process_cmd();
+		break;
+	case EDIT_KEY_CTRL_K:
+		clear_right();
+		break;
+	case EDIT_KEY_CTRL_L:
+		edit_clear_line();
+		edit_redraw();
+		break;
+	case EDIT_KEY_CTRL_R:
+		search = 1;
+		search_start();
+		break;
+	case EDIT_KEY_CTRL_U:
+		clear_left();
+		break;
+	case EDIT_KEY_CTRL_W:
+		delete_word();
+		break;
+	default:
+		if (c >= 32 && c <= 255)
+			insert_char(c);
+		break;
+	}
+}
+
+
+int edit_init(void (*cmd_cb)(void *ctx, char *cmd),
+	      void (*eof_cb)(void *ctx),
+	      char ** (*completion_cb)(void *ctx, const char *cmd, int pos),
+	      void *ctx, const char *history_file, const char *ps)
+{
+	currbuf[0] = '\0';
+	dl_list_init(&history_list);
+	history_curr = NULL;
+	if (history_file)
+		history_read(history_file);
+
+	edit_cb_ctx = ctx;
+	edit_cmd_cb = cmd_cb;
+	edit_eof_cb = eof_cb;
+	edit_completion_cb = completion_cb;
+
+	tcgetattr(STDIN_FILENO, &prevt);
+	newt = prevt;
+	newt.c_lflag &= ~(ICANON | ECHO);
+	tcsetattr(STDIN_FILENO, TCSANOW, &newt);
+
+	eloop_register_read_sock(STDIN_FILENO, edit_read_char, NULL, NULL);
+
+	ps2 = ps;
+	printf("%s> ", ps2 ? ps2 : "");
+	fflush(stdout);
+
+	return 0;
+}
+
+
+void edit_deinit(const char *history_file,
+		 int (*filter_cb)(void *ctx, const char *cmd))
+{
+	struct edit_history *h;
+	if (history_file)
+		history_write(history_file, filter_cb);
+	while ((h = dl_list_first(&history_list, struct edit_history, list))) {
+		dl_list_del(&h->list);
+		os_free(h);
+	}
+	edit_clear_line();
+	putchar('\r');
+	fflush(stdout);
+	eloop_unregister_read_sock(STDIN_FILENO);
+	tcsetattr(STDIN_FILENO, TCSANOW, &prevt);
+}
+
+
+void edit_redraw(void)
+{
+	char tmp;
+	cmdbuf[cmdbuf_len] = '\0';
+	printf("\r%s> %s", ps2 ? ps2 : "", cmdbuf);
+	if (cmdbuf_pos != cmdbuf_len) {
+		tmp = cmdbuf[cmdbuf_pos];
+		cmdbuf[cmdbuf_pos] = '\0';
+		printf("\r%s> %s", ps2 ? ps2 : "", cmdbuf);
+		cmdbuf[cmdbuf_pos] = tmp;
+	}
+	fflush(stdout);
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/edit.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/edit.h
new file mode 100644
index 0000000..ad27f1c
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/edit.h
@@ -0,0 +1,21 @@
+/*
+ * Command line editing and history
+ * Copyright (c) 2010, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef EDIT_H
+#define EDIT_H
+
+int edit_init(void (*cmd_cb)(void *ctx, char *cmd),
+	      void (*eof_cb)(void *ctx),
+	      char ** (*completion_cb)(void *ctx, const char *cmd, int pos),
+	      void *ctx, const char *history_file, const char *ps);
+void edit_deinit(const char *history_file,
+		 int (*filter_cb)(void *ctx, const char *cmd));
+void edit_clear_line(void);
+void edit_redraw(void);
+
+#endif /* EDIT_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/edit_readline.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/edit_readline.c
new file mode 100644
index 0000000..c2a5bca
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/edit_readline.c
@@ -0,0 +1,192 @@
+/*
+ * Command line editing and history wrapper for readline
+ * Copyright (c) 2010, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <readline/readline.h>
+#include <readline/history.h>
+
+#include "common.h"
+#include "eloop.h"
+#include "edit.h"
+
+
+static void *edit_cb_ctx;
+static void (*edit_cmd_cb)(void *ctx, char *cmd);
+static void (*edit_eof_cb)(void *ctx);
+static char ** (*edit_completion_cb)(void *ctx, const char *cmd, int pos) =
+	NULL;
+
+static char **pending_completions = NULL;
+
+
+static void readline_free_completions(void)
+{
+	int i;
+	if (pending_completions == NULL)
+		return;
+	for (i = 0; pending_completions[i]; i++)
+		os_free(pending_completions[i]);
+	os_free(pending_completions);
+	pending_completions = NULL;
+}
+
+
+static char * readline_completion_func(const char *text, int state)
+{
+	static int pos = 0;
+	static size_t len = 0;
+
+	if (pending_completions == NULL) {
+		rl_attempted_completion_over = 1;
+		return NULL;
+	}
+
+	if (state == 0) {
+		pos = 0;
+		len = os_strlen(text);
+	}
+	for (; pending_completions[pos]; pos++) {
+		if (strncmp(pending_completions[pos], text, len) == 0)
+			return strdup(pending_completions[pos++]);
+	}
+
+	rl_attempted_completion_over = 1;
+	return NULL;
+}
+
+
+static char ** readline_completion(const char *text, int start, int end)
+{
+	readline_free_completions();
+	if (edit_completion_cb)
+		pending_completions = edit_completion_cb(edit_cb_ctx,
+							 rl_line_buffer, end);
+	return rl_completion_matches(text, readline_completion_func);
+}
+
+
+static void edit_read_char(int sock, void *eloop_ctx, void *sock_ctx)
+{
+	rl_callback_read_char();
+}
+
+
+static void trunc_nl(char *str)
+{
+	char *pos = str;
+	while (*pos != '\0') {
+		if (*pos == '\n') {
+			*pos = '\0';
+			break;
+		}
+		pos++;
+	}
+}
+
+
+static void readline_cmd_handler(char *cmd)
+{
+	if (cmd && *cmd) {
+		HIST_ENTRY *h;
+		while (next_history())
+			;
+		h = previous_history();
+		if (h == NULL || os_strcmp(cmd, h->line) != 0)
+			add_history(cmd);
+		next_history();
+	}
+	if (cmd == NULL) {
+		edit_eof_cb(edit_cb_ctx);
+		return;
+	}
+	trunc_nl(cmd);
+	edit_cmd_cb(edit_cb_ctx, cmd);
+}
+
+
+int edit_init(void (*cmd_cb)(void *ctx, char *cmd),
+	      void (*eof_cb)(void *ctx),
+	      char ** (*completion_cb)(void *ctx, const char *cmd, int pos),
+	      void *ctx, const char *history_file, const char *ps)
+{
+	edit_cb_ctx = ctx;
+	edit_cmd_cb = cmd_cb;
+	edit_eof_cb = eof_cb;
+	edit_completion_cb = completion_cb;
+
+	rl_attempted_completion_function = readline_completion;
+	if (history_file) {
+		read_history(history_file);
+		stifle_history(100);
+	}
+
+	eloop_register_read_sock(STDIN_FILENO, edit_read_char, NULL, NULL);
+
+	if (ps) {
+		size_t blen = os_strlen(ps) + 3;
+		char *ps2 = os_malloc(blen);
+		if (ps2) {
+			os_snprintf(ps2, blen, "%s> ", ps);
+			rl_callback_handler_install(ps2, readline_cmd_handler);
+			os_free(ps2);
+			return 0;
+		}
+	}
+
+	rl_callback_handler_install("> ", readline_cmd_handler);
+
+	return 0;
+}
+
+
+void edit_deinit(const char *history_file,
+		 int (*filter_cb)(void *ctx, const char *cmd))
+{
+	rl_set_prompt("");
+	rl_replace_line("", 0);
+	rl_redisplay();
+	rl_callback_handler_remove();
+	readline_free_completions();
+
+	eloop_unregister_read_sock(STDIN_FILENO);
+
+	if (history_file) {
+		/* Save command history, excluding lines that may contain
+		 * passwords. */
+		HIST_ENTRY *h;
+		history_set_pos(0);
+		while ((h = current_history())) {
+			char *p = h->line;
+			while (*p == ' ' || *p == '\t')
+				p++;
+			if (filter_cb && filter_cb(edit_cb_ctx, p)) {
+				h = remove_history(where_history());
+				if (h) {
+					free(h->line);
+					free(h->data);
+					free(h);
+				} else
+					next_history();
+			} else
+				next_history();
+		}
+		write_history(history_file);
+	}
+}
+
+
+void edit_clear_line(void)
+{
+}
+
+
+void edit_redraw(void)
+{
+	rl_on_new_line();
+	rl_redisplay();
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/edit_simple.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/edit_simple.c
new file mode 100644
index 0000000..2ffd1a2
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/edit_simple.c
@@ -0,0 +1,98 @@
+/*
+ * Minimal command line editing
+ * Copyright (c) 2010, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eloop.h"
+#include "edit.h"
+
+
+#define CMD_BUF_LEN 4096
+static char cmdbuf[CMD_BUF_LEN];
+static int cmdbuf_pos = 0;
+static const char *ps2 = NULL;
+
+static void *edit_cb_ctx;
+static void (*edit_cmd_cb)(void *ctx, char *cmd);
+static void (*edit_eof_cb)(void *ctx);
+
+
+static void edit_read_char(int sock, void *eloop_ctx, void *sock_ctx)
+{
+	int c;
+	unsigned char buf[1];
+	int res;
+
+	res = read(sock, buf, 1);
+	if (res < 0)
+		perror("read");
+	if (res <= 0) {
+		edit_eof_cb(edit_cb_ctx);
+		return;
+	}
+	c = buf[0];
+
+	if (c == '\r' || c == '\n') {
+		cmdbuf[cmdbuf_pos] = '\0';
+		cmdbuf_pos = 0;
+		edit_cmd_cb(edit_cb_ctx, cmdbuf);
+		printf("%s> ", ps2 ? ps2 : "");
+		fflush(stdout);
+		return;
+	}
+
+	if (c == '\b') {
+		if (cmdbuf_pos > 0)
+			cmdbuf_pos--;
+		return;
+	}
+
+	if (c >= 32 && c <= 255) {
+		if (cmdbuf_pos < (int) sizeof(cmdbuf) - 1) {
+			cmdbuf[cmdbuf_pos++] = c;
+		}
+	}
+}
+
+
+int edit_init(void (*cmd_cb)(void *ctx, char *cmd),
+	      void (*eof_cb)(void *ctx),
+	      char ** (*completion_cb)(void *ctx, const char *cmd, int pos),
+	      void *ctx, const char *history_file, const char *ps)
+{
+	edit_cb_ctx = ctx;
+	edit_cmd_cb = cmd_cb;
+	edit_eof_cb = eof_cb;
+	eloop_register_read_sock(STDIN_FILENO, edit_read_char, NULL, NULL);
+	ps2 = ps;
+
+	printf("%s> ", ps2 ? ps2 : "");
+	fflush(stdout);
+
+	return 0;
+}
+
+
+void edit_deinit(const char *history_file,
+		 int (*filter_cb)(void *ctx, const char *cmd))
+{
+	eloop_unregister_read_sock(STDIN_FILENO);
+}
+
+
+void edit_clear_line(void)
+{
+}
+
+
+void edit_redraw(void)
+{
+	cmdbuf[cmdbuf_pos] = '\0';
+	printf("\r> %s", cmdbuf);
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/eloop.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/eloop.c
new file mode 100644
index 0000000..00b0bef
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/eloop.c
@@ -0,0 +1,1359 @@
+/*
+ * Event loop based on select() loop
+ * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <assert.h>
+
+#include "common.h"
+#include "trace.h"
+#include "list.h"
+#include "eloop.h"
+
+#if defined(CONFIG_ELOOP_POLL) && defined(CONFIG_ELOOP_EPOLL)
+#error Do not define both of poll and epoll
+#endif
+
+#if defined(CONFIG_ELOOP_POLL) && defined(CONFIG_ELOOP_KQUEUE)
+#error Do not define both of poll and kqueue
+#endif
+
+#if !defined(CONFIG_ELOOP_POLL) && !defined(CONFIG_ELOOP_EPOLL) && \
+    !defined(CONFIG_ELOOP_KQUEUE)
+#define CONFIG_ELOOP_SELECT
+#endif
+
+#ifdef CONFIG_ELOOP_POLL
+#include <poll.h>
+#endif /* CONFIG_ELOOP_POLL */
+
+#ifdef CONFIG_ELOOP_EPOLL
+#include <sys/epoll.h>
+#endif /* CONFIG_ELOOP_EPOLL */
+
+#ifdef CONFIG_ELOOP_KQUEUE
+#include <sys/event.h>
+#endif /* CONFIG_ELOOP_KQUEUE */
+
+struct eloop_sock {
+	int sock;
+	void *eloop_data;
+	void *user_data;
+	eloop_sock_handler handler;
+	WPA_TRACE_REF(eloop);
+	WPA_TRACE_REF(user);
+	WPA_TRACE_INFO
+};
+
+struct eloop_timeout {
+	struct dl_list list;
+	struct os_reltime time;
+	void *eloop_data;
+	void *user_data;
+	eloop_timeout_handler handler;
+	WPA_TRACE_REF(eloop);
+	WPA_TRACE_REF(user);
+	WPA_TRACE_INFO
+};
+
+struct eloop_signal {
+	int sig;
+	void *user_data;
+	eloop_signal_handler handler;
+	int signaled;
+};
+
+struct eloop_sock_table {
+	size_t count;
+	struct eloop_sock *table;
+	eloop_event_type type;
+	int changed;
+};
+
+struct eloop_data {
+	int max_sock;
+
+	size_t count; /* sum of all table counts */
+#ifdef CONFIG_ELOOP_POLL
+	size_t max_pollfd_map; /* number of pollfds_map currently allocated */
+	size_t max_poll_fds; /* number of pollfds currently allocated */
+	struct pollfd *pollfds;
+	struct pollfd **pollfds_map;
+#endif /* CONFIG_ELOOP_POLL */
+#if defined(CONFIG_ELOOP_EPOLL) || defined(CONFIG_ELOOP_KQUEUE)
+	int max_fd;
+	struct eloop_sock *fd_table;
+#endif /* CONFIG_ELOOP_EPOLL || CONFIG_ELOOP_KQUEUE */
+#ifdef CONFIG_ELOOP_EPOLL
+	int epollfd;
+	size_t epoll_max_event_num;
+	struct epoll_event *epoll_events;
+#endif /* CONFIG_ELOOP_EPOLL */
+#ifdef CONFIG_ELOOP_KQUEUE
+	int kqueuefd;
+	size_t kqueue_nevents;
+	struct kevent *kqueue_events;
+#endif /* CONFIG_ELOOP_KQUEUE */
+	struct eloop_sock_table readers;
+	struct eloop_sock_table writers;
+	struct eloop_sock_table exceptions;
+
+	struct dl_list timeout;
+
+	size_t signal_count;
+	struct eloop_signal *signals;
+	int signaled;
+	int pending_terminate;
+
+	int terminate;
+};
+
+static struct eloop_data eloop;
+
+
+#ifdef WPA_TRACE
+
+static void eloop_sigsegv_handler(int sig)
+{
+	wpa_trace_show("eloop SIGSEGV");
+	abort();
+}
+
+static void eloop_trace_sock_add_ref(struct eloop_sock_table *table)
+{
+	size_t i;
+
+	if (table == NULL || table->table == NULL)
+		return;
+	for (i = 0; i < table->count; i++) {
+		wpa_trace_add_ref(&table->table[i], eloop,
+				  table->table[i].eloop_data);
+		wpa_trace_add_ref(&table->table[i], user,
+				  table->table[i].user_data);
+	}
+}
+
+
+static void eloop_trace_sock_remove_ref(struct eloop_sock_table *table)
+{
+	size_t i;
+
+	if (table == NULL || table->table == NULL)
+		return;
+	for (i = 0; i < table->count; i++) {
+		wpa_trace_remove_ref(&table->table[i], eloop,
+				     table->table[i].eloop_data);
+		wpa_trace_remove_ref(&table->table[i], user,
+				     table->table[i].user_data);
+	}
+}
+
+#else /* WPA_TRACE */
+
+#define eloop_trace_sock_add_ref(table) do { } while (0)
+#define eloop_trace_sock_remove_ref(table) do { } while (0)
+
+#endif /* WPA_TRACE */
+
+
+int eloop_init(void)
+{
+	os_memset(&eloop, 0, sizeof(eloop));
+	dl_list_init(&eloop.timeout);
+#ifdef CONFIG_ELOOP_EPOLL
+	eloop.epollfd = epoll_create1(0);
+	if (eloop.epollfd < 0) {
+		wpa_printf(MSG_ERROR, "%s: epoll_create1 failed. %s",
+			   __func__, strerror(errno));
+		return -1;
+	}
+#endif /* CONFIG_ELOOP_EPOLL */
+#ifdef CONFIG_ELOOP_KQUEUE
+	eloop.kqueuefd = kqueue();
+	if (eloop.kqueuefd < 0) {
+		wpa_printf(MSG_ERROR, "%s: kqueue failed: %s",
+			   __func__, strerror(errno));
+		return -1;
+	}
+#endif /* CONFIG_ELOOP_KQUEUE */
+#if defined(CONFIG_ELOOP_EPOLL) || defined(CONFIG_ELOOP_KQUEUE)
+	eloop.readers.type = EVENT_TYPE_READ;
+	eloop.writers.type = EVENT_TYPE_WRITE;
+	eloop.exceptions.type = EVENT_TYPE_EXCEPTION;
+#endif /* CONFIG_ELOOP_EPOLL || CONFIG_ELOOP_KQUEUE */
+#ifdef WPA_TRACE
+	signal(SIGSEGV, eloop_sigsegv_handler);
+#endif /* WPA_TRACE */
+	return 0;
+}
+
+
+#ifdef CONFIG_ELOOP_EPOLL
+static int eloop_sock_queue(int sock, eloop_event_type type)
+{
+	struct epoll_event ev;
+
+	os_memset(&ev, 0, sizeof(ev));
+	switch (type) {
+	case EVENT_TYPE_READ:
+		ev.events = EPOLLIN;
+		break;
+	case EVENT_TYPE_WRITE:
+		ev.events = EPOLLOUT;
+		break;
+	/*
+	 * Exceptions are always checked when using epoll, but I suppose it's
+	 * possible that someone registered a socket *only* for exception
+	 * handling.
+	 */
+	case EVENT_TYPE_EXCEPTION:
+		ev.events = EPOLLERR | EPOLLHUP;
+		break;
+	}
+	ev.data.fd = sock;
+	if (epoll_ctl(eloop.epollfd, EPOLL_CTL_ADD, sock, &ev) < 0) {
+		wpa_printf(MSG_ERROR, "%s: epoll_ctl(ADD) for fd=%d failed: %s",
+			   __func__, sock, strerror(errno));
+		return -1;
+	}
+	return 0;
+}
+#endif /* CONFIG_ELOOP_EPOLL */
+
+
+#ifdef CONFIG_ELOOP_KQUEUE
+
+static short event_type_kevent_filter(eloop_event_type type)
+{
+	switch (type) {
+	case EVENT_TYPE_READ:
+		return EVFILT_READ;
+	case EVENT_TYPE_WRITE:
+		return EVFILT_WRITE;
+	default:
+		return 0;
+	}
+}
+
+
+static int eloop_sock_queue(int sock, eloop_event_type type)
+{
+	struct kevent ke;
+
+	EV_SET(&ke, sock, event_type_kevent_filter(type), EV_ADD, 0, 0, 0);
+	if (kevent(eloop.kqueuefd, &ke, 1, NULL, 0, NULL) == -1) {
+		wpa_printf(MSG_ERROR, "%s: kevent(ADD) for fd=%d failed: %s",
+			   __func__, sock, strerror(errno));
+		return -1;
+	}
+	return 0;
+}
+
+#endif /* CONFIG_ELOOP_KQUEUE */
+
+
+static int eloop_sock_table_add_sock(struct eloop_sock_table *table,
+                                     int sock, eloop_sock_handler handler,
+                                     void *eloop_data, void *user_data)
+{
+#ifdef CONFIG_ELOOP_EPOLL
+	struct epoll_event *temp_events;
+#endif /* CONFIG_ELOOP_EPOLL */
+#ifdef CONFIG_ELOOP_KQUEUE
+	struct kevent *temp_events;
+#endif /* CONFIG_ELOOP_EPOLL */
+#if defined(CONFIG_ELOOP_EPOLL) || defined(CONFIG_ELOOP_KQUEUE)
+	struct eloop_sock *temp_table;
+	size_t next;
+#endif /* CONFIG_ELOOP_EPOLL || CONFIG_ELOOP_KQUEUE */
+	struct eloop_sock *tmp;
+	int new_max_sock;
+
+	if (sock > eloop.max_sock)
+		new_max_sock = sock;
+	else
+		new_max_sock = eloop.max_sock;
+
+	if (table == NULL)
+		return -1;
+
+#ifdef CONFIG_ELOOP_POLL
+	if ((size_t) new_max_sock >= eloop.max_pollfd_map) {
+		struct pollfd **nmap;
+		nmap = os_realloc_array(eloop.pollfds_map, new_max_sock + 50,
+					sizeof(struct pollfd *));
+		if (nmap == NULL)
+			return -1;
+
+		eloop.max_pollfd_map = new_max_sock + 50;
+		eloop.pollfds_map = nmap;
+	}
+
+	if (eloop.count + 1 > eloop.max_poll_fds) {
+		struct pollfd *n;
+		size_t nmax = eloop.count + 1 + 50;
+
+		n = os_realloc_array(eloop.pollfds, nmax,
+				     sizeof(struct pollfd));
+		if (n == NULL)
+			return -1;
+
+		eloop.max_poll_fds = nmax;
+		eloop.pollfds = n;
+	}
+#endif /* CONFIG_ELOOP_POLL */
+#if defined(CONFIG_ELOOP_EPOLL) || defined(CONFIG_ELOOP_KQUEUE)
+	if (new_max_sock >= eloop.max_fd) {
+		next = new_max_sock + 16;
+		temp_table = os_realloc_array(eloop.fd_table, next,
+					      sizeof(struct eloop_sock));
+		if (temp_table == NULL)
+			return -1;
+
+		eloop.max_fd = next;
+		eloop.fd_table = temp_table;
+	}
+#endif /* CONFIG_ELOOP_EPOLL || CONFIG_ELOOP_KQUEUE */
+
+#ifdef CONFIG_ELOOP_EPOLL
+	if (eloop.count + 1 > eloop.epoll_max_event_num) {
+		next = eloop.epoll_max_event_num == 0 ? 8 :
+			eloop.epoll_max_event_num * 2;
+		temp_events = os_realloc_array(eloop.epoll_events, next,
+					       sizeof(struct epoll_event));
+		if (temp_events == NULL) {
+			wpa_printf(MSG_ERROR, "%s: malloc for epoll failed: %s",
+				   __func__, strerror(errno));
+			return -1;
+		}
+
+		eloop.epoll_max_event_num = next;
+		eloop.epoll_events = temp_events;
+	}
+#endif /* CONFIG_ELOOP_EPOLL */
+#ifdef CONFIG_ELOOP_KQUEUE
+	if (eloop.count + 1 > eloop.kqueue_nevents) {
+		next = eloop.kqueue_nevents == 0 ? 8 : eloop.kqueue_nevents * 2;
+		temp_events = os_malloc(next * sizeof(*temp_events));
+		if (!temp_events) {
+			wpa_printf(MSG_ERROR,
+				   "%s: malloc for kqueue failed: %s",
+				   __func__, strerror(errno));
+			return -1;
+		}
+
+		os_free(eloop.kqueue_events);
+		eloop.kqueue_events = temp_events;
+		eloop.kqueue_nevents = next;
+	}
+#endif /* CONFIG_ELOOP_KQUEUE */
+
+	eloop_trace_sock_remove_ref(table);
+	tmp = os_realloc_array(table->table, table->count + 1,
+			       sizeof(struct eloop_sock));
+	if (tmp == NULL) {
+		eloop_trace_sock_add_ref(table);
+		return -1;
+	}
+
+	tmp[table->count].sock = sock;
+	tmp[table->count].eloop_data = eloop_data;
+	tmp[table->count].user_data = user_data;
+	tmp[table->count].handler = handler;
+	wpa_trace_record(&tmp[table->count]);
+	table->count++;
+	table->table = tmp;
+	eloop.max_sock = new_max_sock;
+	eloop.count++;
+	table->changed = 1;
+	eloop_trace_sock_add_ref(table);
+
+#if defined(CONFIG_ELOOP_EPOLL) || defined(CONFIG_ELOOP_KQUEUE)
+	if (eloop_sock_queue(sock, table->type) < 0)
+		return -1;
+	os_memcpy(&eloop.fd_table[sock], &table->table[table->count - 1],
+		  sizeof(struct eloop_sock));
+#endif /* CONFIG_ELOOP_EPOLL || CONFIG_ELOOP_KQUEUE */
+	return 0;
+}
+
+
+static void eloop_sock_table_remove_sock(struct eloop_sock_table *table,
+                                         int sock)
+{
+#ifdef CONFIG_ELOOP_KQUEUE
+	struct kevent ke;
+#endif /* CONFIG_ELOOP_KQUEUE */
+	size_t i;
+
+	if (table == NULL || table->table == NULL || table->count == 0)
+		return;
+
+	for (i = 0; i < table->count; i++) {
+		if (table->table[i].sock == sock)
+			break;
+	}
+	if (i == table->count)
+		return;
+	eloop_trace_sock_remove_ref(table);
+	if (i != table->count - 1) {
+		os_memmove(&table->table[i], &table->table[i + 1],
+			   (table->count - i - 1) *
+			   sizeof(struct eloop_sock));
+	}
+	table->count--;
+	eloop.count--;
+	table->changed = 1;
+	eloop_trace_sock_add_ref(table);
+#ifdef CONFIG_ELOOP_EPOLL
+	if (epoll_ctl(eloop.epollfd, EPOLL_CTL_DEL, sock, NULL) < 0) {
+		wpa_printf(MSG_ERROR, "%s: epoll_ctl(DEL) for fd=%d failed: %s",
+			   __func__, sock, strerror(errno));
+		return;
+	}
+	os_memset(&eloop.fd_table[sock], 0, sizeof(struct eloop_sock));
+#endif /* CONFIG_ELOOP_EPOLL */
+#ifdef CONFIG_ELOOP_KQUEUE
+	EV_SET(&ke, sock, event_type_kevent_filter(table->type), EV_DELETE, 0,
+	       0, 0);
+	if (kevent(eloop.kqueuefd, &ke, 1, NULL, 0, NULL) < 0) {
+		wpa_printf(MSG_ERROR, "%s: kevent(DEL) for fd=%d failed: %s",
+			   __func__, sock, strerror(errno));
+		return;
+	}
+	os_memset(&eloop.fd_table[sock], 0, sizeof(struct eloop_sock));
+#endif /* CONFIG_ELOOP_KQUEUE */
+}
+
+
+#ifdef CONFIG_ELOOP_POLL
+
+static struct pollfd * find_pollfd(struct pollfd **pollfds_map, int fd, int mx)
+{
+	if (fd < mx && fd >= 0)
+		return pollfds_map[fd];
+	return NULL;
+}
+
+
+static int eloop_sock_table_set_fds(struct eloop_sock_table *readers,
+				    struct eloop_sock_table *writers,
+				    struct eloop_sock_table *exceptions,
+				    struct pollfd *pollfds,
+				    struct pollfd **pollfds_map,
+				    int max_pollfd_map)
+{
+	size_t i;
+	int nxt = 0;
+	int fd;
+	struct pollfd *pfd;
+
+	/* Clear pollfd lookup map. It will be re-populated below. */
+	os_memset(pollfds_map, 0, sizeof(struct pollfd *) * max_pollfd_map);
+
+	if (readers && readers->table) {
+		for (i = 0; i < readers->count; i++) {
+			fd = readers->table[i].sock;
+			assert(fd >= 0 && fd < max_pollfd_map);
+			pollfds[nxt].fd = fd;
+			pollfds[nxt].events = POLLIN;
+			pollfds[nxt].revents = 0;
+			pollfds_map[fd] = &(pollfds[nxt]);
+			nxt++;
+		}
+	}
+
+	if (writers && writers->table) {
+		for (i = 0; i < writers->count; i++) {
+			/*
+			 * See if we already added this descriptor, update it
+			 * if so.
+			 */
+			fd = writers->table[i].sock;
+			assert(fd >= 0 && fd < max_pollfd_map);
+			pfd = pollfds_map[fd];
+			if (!pfd) {
+				pfd = &(pollfds[nxt]);
+				pfd->events = 0;
+				pfd->fd = fd;
+				pollfds[i].revents = 0;
+				pollfds_map[fd] = pfd;
+				nxt++;
+			}
+			pfd->events |= POLLOUT;
+		}
+	}
+
+	/*
+	 * Exceptions are always checked when using poll, but I suppose it's
+	 * possible that someone registered a socket *only* for exception
+	 * handling. Set the POLLIN bit in this case.
+	 */
+	if (exceptions && exceptions->table) {
+		for (i = 0; i < exceptions->count; i++) {
+			/*
+			 * See if we already added this descriptor, just use it
+			 * if so.
+			 */
+			fd = exceptions->table[i].sock;
+			assert(fd >= 0 && fd < max_pollfd_map);
+			pfd = pollfds_map[fd];
+			if (!pfd) {
+				pfd = &(pollfds[nxt]);
+				pfd->events = POLLIN;
+				pfd->fd = fd;
+				pollfds[i].revents = 0;
+				pollfds_map[fd] = pfd;
+				nxt++;
+			}
+		}
+	}
+
+	return nxt;
+}
+
+
+static int eloop_sock_table_dispatch_table(struct eloop_sock_table *table,
+					   struct pollfd **pollfds_map,
+					   int max_pollfd_map,
+					   short int revents)
+{
+	size_t i;
+	struct pollfd *pfd;
+
+	if (!table || !table->table)
+		return 0;
+
+	table->changed = 0;
+	for (i = 0; i < table->count; i++) {
+		pfd = find_pollfd(pollfds_map, table->table[i].sock,
+				  max_pollfd_map);
+		if (!pfd)
+			continue;
+
+		if (!(pfd->revents & revents))
+			continue;
+
+		table->table[i].handler(table->table[i].sock,
+					table->table[i].eloop_data,
+					table->table[i].user_data);
+		if (table->changed)
+			return 1;
+	}
+
+	return 0;
+}
+
+
+static void eloop_sock_table_dispatch(struct eloop_sock_table *readers,
+				      struct eloop_sock_table *writers,
+				      struct eloop_sock_table *exceptions,
+				      struct pollfd **pollfds_map,
+				      int max_pollfd_map)
+{
+	if (eloop_sock_table_dispatch_table(readers, pollfds_map,
+					    max_pollfd_map, POLLIN | POLLERR |
+					    POLLHUP))
+		return; /* pollfds may be invalid at this point */
+
+	if (eloop_sock_table_dispatch_table(writers, pollfds_map,
+					    max_pollfd_map, POLLOUT))
+		return; /* pollfds may be invalid at this point */
+
+	eloop_sock_table_dispatch_table(exceptions, pollfds_map,
+					max_pollfd_map, POLLERR | POLLHUP);
+}
+
+#endif /* CONFIG_ELOOP_POLL */
+
+#ifdef CONFIG_ELOOP_SELECT
+
+static void eloop_sock_table_set_fds(struct eloop_sock_table *table,
+				     fd_set *fds)
+{
+	size_t i;
+
+	FD_ZERO(fds);
+
+	if (table->table == NULL)
+		return;
+
+	for (i = 0; i < table->count; i++) {
+		assert(table->table[i].sock >= 0);
+		FD_SET(table->table[i].sock, fds);
+	}
+}
+
+
+static void eloop_sock_table_dispatch(struct eloop_sock_table *table,
+				      fd_set *fds)
+{
+	size_t i;
+
+	if (table == NULL || table->table == NULL)
+		return;
+
+	table->changed = 0;
+	for (i = 0; i < table->count; i++) {
+		if (FD_ISSET(table->table[i].sock, fds)) {
+			table->table[i].handler(table->table[i].sock,
+						table->table[i].eloop_data,
+						table->table[i].user_data);
+			if (table->changed)
+				break;
+		}
+	}
+}
+
+#endif /* CONFIG_ELOOP_SELECT */
+
+
+#ifdef CONFIG_ELOOP_EPOLL
+static void eloop_sock_table_dispatch(struct epoll_event *events, int nfds)
+{
+	struct eloop_sock *table;
+	int i;
+
+	for (i = 0; i < nfds; i++) {
+		table = &eloop.fd_table[events[i].data.fd];
+		if (table->handler == NULL)
+			continue;
+		table->handler(table->sock, table->eloop_data,
+			       table->user_data);
+		if (eloop.readers.changed ||
+		    eloop.writers.changed ||
+		    eloop.exceptions.changed)
+			break;
+	}
+}
+#endif /* CONFIG_ELOOP_EPOLL */
+
+
+#ifdef CONFIG_ELOOP_KQUEUE
+
+static void eloop_sock_table_dispatch(struct kevent *events, int nfds)
+{
+	struct eloop_sock *table;
+	int i;
+
+	for (i = 0; i < nfds; i++) {
+		table = &eloop.fd_table[events[i].ident];
+		if (table->handler == NULL)
+			continue;
+		table->handler(table->sock, table->eloop_data,
+			       table->user_data);
+		if (eloop.readers.changed ||
+		    eloop.writers.changed ||
+		    eloop.exceptions.changed)
+			break;
+	}
+}
+
+
+static int eloop_sock_table_requeue(struct eloop_sock_table *table)
+{
+	size_t i;
+	int r;
+
+	r = 0;
+	for (i = 0; i < table->count && table->table; i++) {
+		if (eloop_sock_queue(table->table[i].sock, table->type) == -1)
+			r = -1;
+	}
+	return r;
+}
+
+#endif /* CONFIG_ELOOP_KQUEUE */
+
+
+int eloop_sock_requeue(void)
+{
+	int r = 0;
+
+#ifdef CONFIG_ELOOP_KQUEUE
+	close(eloop.kqueuefd);
+	eloop.kqueuefd = kqueue();
+	if (eloop.kqueuefd < 0) {
+		wpa_printf(MSG_ERROR, "%s: kqueue failed: %s",
+			   __func__, strerror(errno));
+		return -1;
+	}
+
+	if (eloop_sock_table_requeue(&eloop.readers) < 0)
+		r = -1;
+	if (eloop_sock_table_requeue(&eloop.writers) < 0)
+		r = -1;
+	if (eloop_sock_table_requeue(&eloop.exceptions) < 0)
+		r = -1;
+#endif /* CONFIG_ELOOP_KQUEUE */
+
+	return r;
+}
+
+
+static void eloop_sock_table_destroy(struct eloop_sock_table *table)
+{
+	if (table) {
+		size_t i;
+
+		for (i = 0; i < table->count && table->table; i++) {
+			wpa_printf(MSG_INFO, "ELOOP: remaining socket: "
+				   "sock=%d eloop_data=%p user_data=%p "
+				   "handler=%p",
+				   table->table[i].sock,
+				   table->table[i].eloop_data,
+				   table->table[i].user_data,
+				   table->table[i].handler);
+			wpa_trace_dump_funcname("eloop unregistered socket "
+						"handler",
+						table->table[i].handler);
+			wpa_trace_dump("eloop sock", &table->table[i]);
+		}
+		os_free(table->table);
+	}
+}
+
+
+int eloop_register_read_sock(int sock, eloop_sock_handler handler,
+			     void *eloop_data, void *user_data)
+{
+	return eloop_register_sock(sock, EVENT_TYPE_READ, handler,
+				   eloop_data, user_data);
+}
+
+
+void eloop_unregister_read_sock(int sock)
+{
+	eloop_unregister_sock(sock, EVENT_TYPE_READ);
+}
+
+
+static struct eloop_sock_table *eloop_get_sock_table(eloop_event_type type)
+{
+	switch (type) {
+	case EVENT_TYPE_READ:
+		return &eloop.readers;
+	case EVENT_TYPE_WRITE:
+		return &eloop.writers;
+	case EVENT_TYPE_EXCEPTION:
+		return &eloop.exceptions;
+	}
+
+	return NULL;
+}
+
+
+int eloop_register_sock(int sock, eloop_event_type type,
+			eloop_sock_handler handler,
+			void *eloop_data, void *user_data)
+{
+	struct eloop_sock_table *table;
+
+	assert(sock >= 0);
+	table = eloop_get_sock_table(type);
+	return eloop_sock_table_add_sock(table, sock, handler,
+					 eloop_data, user_data);
+}
+
+
+void eloop_unregister_sock(int sock, eloop_event_type type)
+{
+	struct eloop_sock_table *table;
+
+	table = eloop_get_sock_table(type);
+	eloop_sock_table_remove_sock(table, sock);
+}
+
+
+int eloop_register_timeout(unsigned int secs, unsigned int usecs,
+			   eloop_timeout_handler handler,
+			   void *eloop_data, void *user_data)
+{
+	struct eloop_timeout *timeout, *tmp;
+	os_time_t now_sec;
+
+	timeout = os_zalloc(sizeof(*timeout));
+	if (timeout == NULL)
+		return -1;
+	if (os_get_reltime(&timeout->time) < 0) {
+		os_free(timeout);
+		return -1;
+	}
+	now_sec = timeout->time.sec;
+	timeout->time.sec += secs;
+	if (timeout->time.sec < now_sec)
+		goto overflow;
+	timeout->time.usec += usecs;
+	while (timeout->time.usec >= 1000000) {
+		timeout->time.sec++;
+		timeout->time.usec -= 1000000;
+	}
+	if (timeout->time.sec < now_sec)
+		goto overflow;
+	timeout->eloop_data = eloop_data;
+	timeout->user_data = user_data;
+	timeout->handler = handler;
+	wpa_trace_add_ref(timeout, eloop, eloop_data);
+	wpa_trace_add_ref(timeout, user, user_data);
+	wpa_trace_record(timeout);
+
+	/* Maintain timeouts in order of increasing time */
+	dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) {
+		if (os_reltime_before(&timeout->time, &tmp->time)) {
+			dl_list_add(tmp->list.prev, &timeout->list);
+			return 0;
+		}
+	}
+	dl_list_add_tail(&eloop.timeout, &timeout->list);
+
+	return 0;
+
+overflow:
+	/*
+	 * Integer overflow - assume long enough timeout to be assumed
+	 * to be infinite, i.e., the timeout would never happen.
+	 */
+	wpa_printf(MSG_DEBUG,
+		   "ELOOP: Too long timeout (secs=%u usecs=%u) to ever happen - ignore it",
+		   secs,usecs);
+	os_free(timeout);
+	return 0;
+}
+
+
+static void eloop_remove_timeout(struct eloop_timeout *timeout)
+{
+	dl_list_del(&timeout->list);
+	wpa_trace_remove_ref(timeout, eloop, timeout->eloop_data);
+	wpa_trace_remove_ref(timeout, user, timeout->user_data);
+	os_free(timeout);
+}
+
+
+int eloop_cancel_timeout(eloop_timeout_handler handler,
+			 void *eloop_data, void *user_data)
+{
+	struct eloop_timeout *timeout, *prev;
+	int removed = 0;
+
+	dl_list_for_each_safe(timeout, prev, &eloop.timeout,
+			      struct eloop_timeout, list) {
+		if (timeout->handler == handler &&
+		    (timeout->eloop_data == eloop_data ||
+		     eloop_data == ELOOP_ALL_CTX) &&
+		    (timeout->user_data == user_data ||
+		     user_data == ELOOP_ALL_CTX)) {
+			eloop_remove_timeout(timeout);
+			removed++;
+		}
+	}
+
+	return removed;
+}
+
+
+int eloop_cancel_timeout_one(eloop_timeout_handler handler,
+			     void *eloop_data, void *user_data,
+			     struct os_reltime *remaining)
+{
+	struct eloop_timeout *timeout, *prev;
+	int removed = 0;
+	struct os_reltime now;
+
+	os_get_reltime(&now);
+	remaining->sec = remaining->usec = 0;
+
+	dl_list_for_each_safe(timeout, prev, &eloop.timeout,
+			      struct eloop_timeout, list) {
+		if (timeout->handler == handler &&
+		    (timeout->eloop_data == eloop_data) &&
+		    (timeout->user_data == user_data)) {
+			removed = 1;
+			if (os_reltime_before(&now, &timeout->time))
+				os_reltime_sub(&timeout->time, &now, remaining);
+			eloop_remove_timeout(timeout);
+			break;
+		}
+	}
+	return removed;
+}
+
+
+int eloop_is_timeout_registered(eloop_timeout_handler handler,
+				void *eloop_data, void *user_data)
+{
+	struct eloop_timeout *tmp;
+
+	dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) {
+		if (tmp->handler == handler &&
+		    tmp->eloop_data == eloop_data &&
+		    tmp->user_data == user_data)
+			return 1;
+	}
+
+	return 0;
+}
+
+
+int eloop_deplete_timeout(unsigned int req_secs, unsigned int req_usecs,
+			  eloop_timeout_handler handler, void *eloop_data,
+			  void *user_data)
+{
+	struct os_reltime now, requested, remaining;
+	struct eloop_timeout *tmp;
+
+	dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) {
+		if (tmp->handler == handler &&
+		    tmp->eloop_data == eloop_data &&
+		    tmp->user_data == user_data) {
+			requested.sec = req_secs;
+			requested.usec = req_usecs;
+			os_get_reltime(&now);
+			os_reltime_sub(&tmp->time, &now, &remaining);
+			if (os_reltime_before(&requested, &remaining)) {
+				eloop_cancel_timeout(handler, eloop_data,
+						     user_data);
+				eloop_register_timeout(requested.sec,
+						       requested.usec,
+						       handler, eloop_data,
+						       user_data);
+				return 1;
+			}
+			return 0;
+		}
+	}
+
+	return -1;
+}
+
+
+int eloop_replenish_timeout(unsigned int req_secs, unsigned int req_usecs,
+			    eloop_timeout_handler handler, void *eloop_data,
+			    void *user_data)
+{
+	struct os_reltime now, requested, remaining;
+	struct eloop_timeout *tmp;
+
+	dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) {
+		if (tmp->handler == handler &&
+		    tmp->eloop_data == eloop_data &&
+		    tmp->user_data == user_data) {
+			requested.sec = req_secs;
+			requested.usec = req_usecs;
+			os_get_reltime(&now);
+			os_reltime_sub(&tmp->time, &now, &remaining);
+			if (os_reltime_before(&remaining, &requested)) {
+				eloop_cancel_timeout(handler, eloop_data,
+						     user_data);
+				eloop_register_timeout(requested.sec,
+						       requested.usec,
+						       handler, eloop_data,
+						       user_data);
+				return 1;
+			}
+			return 0;
+		}
+	}
+
+	return -1;
+}
+
+
+#ifndef CONFIG_NATIVE_WINDOWS
+static void eloop_handle_alarm(int sig)
+{
+	wpa_printf(MSG_ERROR, "eloop: could not process SIGINT or SIGTERM in "
+		   "two seconds. Looks like there\n"
+		   "is a bug that ends up in a busy loop that "
+		   "prevents clean shutdown.\n"
+		   "Killing program forcefully.\n");
+	exit(1);
+}
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+
+static void eloop_handle_signal(int sig)
+{
+	size_t i;
+
+#ifndef CONFIG_NATIVE_WINDOWS
+	if ((sig == SIGINT || sig == SIGTERM) && !eloop.pending_terminate) {
+		/* Use SIGALRM to break out from potential busy loops that
+		 * would not allow the program to be killed. */
+		eloop.pending_terminate = 1;
+		signal(SIGALRM, eloop_handle_alarm);
+		alarm(2);
+	}
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+	eloop.signaled++;
+	for (i = 0; i < eloop.signal_count; i++) {
+		if (eloop.signals[i].sig == sig) {
+			eloop.signals[i].signaled++;
+			break;
+		}
+	}
+}
+
+
+static void eloop_process_pending_signals(void)
+{
+	size_t i;
+
+	if (eloop.signaled == 0)
+		return;
+	eloop.signaled = 0;
+
+	if (eloop.pending_terminate) {
+#ifndef CONFIG_NATIVE_WINDOWS
+		alarm(0);
+#endif /* CONFIG_NATIVE_WINDOWS */
+		eloop.pending_terminate = 0;
+	}
+
+	for (i = 0; i < eloop.signal_count; i++) {
+		if (eloop.signals[i].signaled) {
+			eloop.signals[i].signaled = 0;
+			eloop.signals[i].handler(eloop.signals[i].sig,
+						 eloop.signals[i].user_data);
+		}
+	}
+}
+
+
+int eloop_register_signal(int sig, eloop_signal_handler handler,
+			  void *user_data)
+{
+	struct eloop_signal *tmp;
+
+	tmp = os_realloc_array(eloop.signals, eloop.signal_count + 1,
+			       sizeof(struct eloop_signal));
+	if (tmp == NULL)
+		return -1;
+
+	tmp[eloop.signal_count].sig = sig;
+	tmp[eloop.signal_count].user_data = user_data;
+	tmp[eloop.signal_count].handler = handler;
+	tmp[eloop.signal_count].signaled = 0;
+	eloop.signal_count++;
+	eloop.signals = tmp;
+	signal(sig, eloop_handle_signal);
+
+	return 0;
+}
+
+
+int eloop_register_signal_terminate(eloop_signal_handler handler,
+				    void *user_data)
+{
+	int ret = eloop_register_signal(SIGINT, handler, user_data);
+	if (ret == 0)
+		ret = eloop_register_signal(SIGTERM, handler, user_data);
+	return ret;
+}
+
+
+int eloop_register_signal_reconfig(eloop_signal_handler handler,
+				   void *user_data)
+{
+#ifdef CONFIG_NATIVE_WINDOWS
+	return 0;
+#else /* CONFIG_NATIVE_WINDOWS */
+	return eloop_register_signal(SIGHUP, handler, user_data);
+#endif /* CONFIG_NATIVE_WINDOWS */
+}
+
+
+void eloop_run(void)
+{
+#ifdef CONFIG_ELOOP_POLL
+	int num_poll_fds;
+	int timeout_ms = 0;
+#endif /* CONFIG_ELOOP_POLL */
+#ifdef CONFIG_ELOOP_SELECT
+	fd_set *rfds, *wfds, *efds;
+	struct timeval _tv;
+#endif /* CONFIG_ELOOP_SELECT */
+#ifdef CONFIG_ELOOP_EPOLL
+	int timeout_ms = -1;
+#endif /* CONFIG_ELOOP_EPOLL */
+#ifdef CONFIG_ELOOP_KQUEUE
+	struct timespec ts;
+#endif /* CONFIG_ELOOP_KQUEUE */
+	int res;
+	struct os_reltime tv, now;
+
+#ifdef CONFIG_ELOOP_SELECT
+	rfds = os_malloc(sizeof(*rfds));
+	wfds = os_malloc(sizeof(*wfds));
+	efds = os_malloc(sizeof(*efds));
+	if (rfds == NULL || wfds == NULL || efds == NULL)
+		goto out;
+#endif /* CONFIG_ELOOP_SELECT */
+
+	while (!eloop.terminate &&
+	       (!dl_list_empty(&eloop.timeout) || eloop.readers.count > 0 ||
+		eloop.writers.count > 0 || eloop.exceptions.count > 0)) {
+		struct eloop_timeout *timeout;
+
+		if (eloop.pending_terminate) {
+			/*
+			 * This may happen in some corner cases where a signal
+			 * is received during a blocking operation. We need to
+			 * process the pending signals and exit if requested to
+			 * avoid hitting the SIGALRM limit if the blocking
+			 * operation took more than two seconds.
+			 */
+			eloop_process_pending_signals();
+			if (eloop.terminate)
+				break;
+		}
+
+		timeout = dl_list_first(&eloop.timeout, struct eloop_timeout,
+					list);
+		if (timeout) {
+			os_get_reltime(&now);
+			if (os_reltime_before(&now, &timeout->time))
+				os_reltime_sub(&timeout->time, &now, &tv);
+			else
+				tv.sec = tv.usec = 0;
+#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) */
+#ifdef CONFIG_ELOOP_SELECT
+			_tv.tv_sec = tv.sec;
+			_tv.tv_usec = tv.usec;
+#endif /* CONFIG_ELOOP_SELECT */
+#ifdef CONFIG_ELOOP_KQUEUE
+			ts.tv_sec = tv.sec;
+			ts.tv_nsec = tv.usec * 1000L;
+#endif /* CONFIG_ELOOP_KQUEUE */
+		}
+
+#ifdef CONFIG_ELOOP_POLL
+		num_poll_fds = eloop_sock_table_set_fds(
+			&eloop.readers, &eloop.writers, &eloop.exceptions,
+			eloop.pollfds, eloop.pollfds_map,
+			eloop.max_pollfd_map);
+		res = poll(eloop.pollfds, num_poll_fds,
+			   timeout ? timeout_ms : -1);
+#endif /* CONFIG_ELOOP_POLL */
+#ifdef CONFIG_ELOOP_SELECT
+		eloop_sock_table_set_fds(&eloop.readers, rfds);
+		eloop_sock_table_set_fds(&eloop.writers, wfds);
+		eloop_sock_table_set_fds(&eloop.exceptions, efds);
+		res = select(eloop.max_sock + 1, rfds, wfds, efds,
+			     timeout ? &_tv : NULL);
+#endif /* CONFIG_ELOOP_SELECT */
+#ifdef CONFIG_ELOOP_EPOLL
+		if (eloop.count == 0) {
+			res = 0;
+		} else {
+			res = epoll_wait(eloop.epollfd, eloop.epoll_events,
+					 eloop.count, timeout_ms);
+		}
+#endif /* CONFIG_ELOOP_EPOLL */
+#ifdef CONFIG_ELOOP_KQUEUE
+		if (eloop.count == 0) {
+			res = 0;
+		} else {
+			res = kevent(eloop.kqueuefd, NULL, 0,
+				     eloop.kqueue_events, eloop.kqueue_nevents,
+				     timeout ? &ts : NULL);
+		}
+#endif /* CONFIG_ELOOP_KQUEUE */
+		if (res < 0 && errno != EINTR && errno != 0) {
+			wpa_printf(MSG_ERROR, "eloop: %s: %s",
+#ifdef CONFIG_ELOOP_POLL
+				   "poll"
+#endif /* CONFIG_ELOOP_POLL */
+#ifdef CONFIG_ELOOP_SELECT
+				   "select"
+#endif /* CONFIG_ELOOP_SELECT */
+#ifdef CONFIG_ELOOP_EPOLL
+				   "epoll"
+#endif /* CONFIG_ELOOP_EPOLL */
+#ifdef CONFIG_ELOOP_KQUEUE
+				   "kqueue"
+#endif /* CONFIG_ELOOP_EKQUEUE */
+
+				   , strerror(errno));
+			goto out;
+		}
+
+		eloop.readers.changed = 0;
+		eloop.writers.changed = 0;
+		eloop.exceptions.changed = 0;
+
+		eloop_process_pending_signals();
+
+
+		/* check if some registered timeouts have occurred */
+		timeout = dl_list_first(&eloop.timeout, struct eloop_timeout,
+					list);
+		if (timeout) {
+			os_get_reltime(&now);
+			if (!os_reltime_before(&now, &timeout->time)) {
+				void *eloop_data = timeout->eloop_data;
+				void *user_data = timeout->user_data;
+				eloop_timeout_handler handler =
+					timeout->handler;
+				eloop_remove_timeout(timeout);
+				handler(eloop_data, user_data);
+			}
+
+		}
+
+		if (res <= 0)
+			continue;
+
+		if (eloop.readers.changed ||
+		    eloop.writers.changed ||
+		    eloop.exceptions.changed) {
+			 /*
+			  * Sockets may have been closed and reopened with the
+			  * same FD in the signal or timeout handlers, so we
+			  * must skip the previous results and check again
+			  * whether any of the currently registered sockets have
+			  * events.
+			  */
+			continue;
+		}
+
+#ifdef CONFIG_ELOOP_POLL
+		eloop_sock_table_dispatch(&eloop.readers, &eloop.writers,
+					  &eloop.exceptions, eloop.pollfds_map,
+					  eloop.max_pollfd_map);
+#endif /* CONFIG_ELOOP_POLL */
+#ifdef CONFIG_ELOOP_SELECT
+		eloop_sock_table_dispatch(&eloop.readers, rfds);
+		eloop_sock_table_dispatch(&eloop.writers, wfds);
+		eloop_sock_table_dispatch(&eloop.exceptions, efds);
+#endif /* CONFIG_ELOOP_SELECT */
+#ifdef CONFIG_ELOOP_EPOLL
+		eloop_sock_table_dispatch(eloop.epoll_events, res);
+#endif /* CONFIG_ELOOP_EPOLL */
+#ifdef CONFIG_ELOOP_KQUEUE
+		eloop_sock_table_dispatch(eloop.kqueue_events, res);
+#endif /* CONFIG_ELOOP_KQUEUE */
+	}
+
+	eloop.terminate = 0;
+out:
+#ifdef CONFIG_ELOOP_SELECT
+	os_free(rfds);
+	os_free(wfds);
+	os_free(efds);
+#endif /* CONFIG_ELOOP_SELECT */
+	return;
+}
+
+
+void eloop_terminate(void)
+{
+	eloop.terminate = 1;
+}
+
+
+void eloop_destroy(void)
+{
+	struct eloop_timeout *timeout, *prev;
+	struct os_reltime now;
+
+	os_get_reltime(&now);
+	dl_list_for_each_safe(timeout, prev, &eloop.timeout,
+			      struct eloop_timeout, list) {
+		int sec, usec;
+		sec = timeout->time.sec - now.sec;
+		usec = timeout->time.usec - now.usec;
+		if (timeout->time.usec < now.usec) {
+			sec--;
+			usec += 1000000;
+		}
+		wpa_printf(MSG_INFO, "ELOOP: remaining timeout: %d.%06d "
+			   "eloop_data=%p user_data=%p handler=%p",
+			   sec, usec, timeout->eloop_data, timeout->user_data,
+			   timeout->handler);
+		wpa_trace_dump_funcname("eloop unregistered timeout handler",
+					timeout->handler);
+		wpa_trace_dump("eloop timeout", timeout);
+		eloop_remove_timeout(timeout);
+	}
+	eloop_sock_table_destroy(&eloop.readers);
+	eloop_sock_table_destroy(&eloop.writers);
+	eloop_sock_table_destroy(&eloop.exceptions);
+	os_free(eloop.signals);
+
+#ifdef CONFIG_ELOOP_POLL
+	os_free(eloop.pollfds);
+	os_free(eloop.pollfds_map);
+#endif /* CONFIG_ELOOP_POLL */
+#if defined(CONFIG_ELOOP_EPOLL) || defined(CONFIG_ELOOP_KQUEUE)
+	os_free(eloop.fd_table);
+#endif /* CONFIG_ELOOP_EPOLL || CONFIG_ELOOP_KQUEUE */
+#ifdef CONFIG_ELOOP_EPOLL
+	os_free(eloop.epoll_events);
+	close(eloop.epollfd);
+#endif /* CONFIG_ELOOP_EPOLL */
+#ifdef CONFIG_ELOOP_KQUEUE
+	os_free(eloop.kqueue_events);
+	close(eloop.kqueuefd);
+#endif /* CONFIG_ELOOP_KQUEUE */
+}
+
+
+int eloop_terminated(void)
+{
+	return eloop.terminate || eloop.pending_terminate;
+}
+
+
+void eloop_wait_for_read_sock(int sock)
+{
+#ifdef CONFIG_ELOOP_POLL
+	struct pollfd pfd;
+
+	if (sock < 0)
+		return;
+
+	os_memset(&pfd, 0, sizeof(pfd));
+	pfd.fd = sock;
+	pfd.events = POLLIN;
+
+	poll(&pfd, 1, -1);
+#endif /* CONFIG_ELOOP_POLL */
+#if defined(CONFIG_ELOOP_SELECT) || defined(CONFIG_ELOOP_EPOLL)
+	/*
+	 * We can use epoll() here. But epoll() requres 4 system calls.
+	 * epoll_create1(), epoll_ctl() for ADD, epoll_wait, and close() for
+	 * epoll fd. So select() is better for performance here.
+	 */
+	fd_set rfds;
+
+	if (sock < 0)
+		return;
+
+	FD_ZERO(&rfds);
+	FD_SET(sock, &rfds);
+	select(sock + 1, &rfds, NULL, NULL, NULL);
+#endif /* defined(CONFIG_ELOOP_SELECT) || defined(CONFIG_ELOOP_EPOLL) */
+#ifdef CONFIG_ELOOP_KQUEUE
+	int kfd;
+	struct kevent ke1, ke2;
+
+	kfd = kqueue();
+	if (kfd == -1)
+		return;
+	EV_SET(&ke1, sock, EVFILT_READ, EV_ADD | EV_ONESHOT, 0, 0, 0);
+	kevent(kfd, &ke1, 1, &ke2, 1, NULL);
+	close(kfd);
+#endif /* CONFIG_ELOOP_KQUEUE */
+}
+
+#ifdef CONFIG_ELOOP_SELECT
+#undef CONFIG_ELOOP_SELECT
+#endif /* CONFIG_ELOOP_SELECT */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/eloop.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/eloop.h
new file mode 100644
index 0000000..04ee6d1
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/eloop.h
@@ -0,0 +1,367 @@
+/*
+ * Event loop
+ * Copyright (c) 2002-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ *
+ * This file defines an event loop interface that supports processing events
+ * from registered timeouts (i.e., do something after N seconds), sockets
+ * (e.g., a new packet available for reading), and signals. eloop.c is an
+ * implementation of this interface using select() and sockets. This is
+ * suitable for most UNIX/POSIX systems. When porting to other operating
+ * systems, it may be necessary to replace that implementation with OS specific
+ * mechanisms.
+ */
+
+#ifndef ELOOP_H
+#define ELOOP_H
+
+/**
+ * ELOOP_ALL_CTX - eloop_cancel_timeout() magic number to match all timeouts
+ */
+#define ELOOP_ALL_CTX (void *) -1
+
+/**
+ * eloop_event_type - eloop socket event type for eloop_register_sock()
+ * @EVENT_TYPE_READ: Socket has data available for reading
+ * @EVENT_TYPE_WRITE: Socket has room for new data to be written
+ * @EVENT_TYPE_EXCEPTION: An exception has been reported
+ */
+typedef enum {
+	EVENT_TYPE_READ = 0,
+	EVENT_TYPE_WRITE,
+	EVENT_TYPE_EXCEPTION
+} eloop_event_type;
+
+/**
+ * eloop_sock_handler - eloop socket event callback type
+ * @sock: File descriptor number for the socket
+ * @eloop_ctx: Registered callback context data (eloop_data)
+ * @sock_ctx: Registered callback context data (user_data)
+ */
+typedef void (*eloop_sock_handler)(int sock, void *eloop_ctx, void *sock_ctx);
+
+/**
+ * eloop_event_handler - eloop generic event callback type
+ * @eloop_ctx: Registered callback context data (eloop_data)
+ * @user_ctx: Registered callback context data (user_data)
+ */
+typedef void (*eloop_event_handler)(void *eloop_ctx, void *user_ctx);
+
+/**
+ * eloop_timeout_handler - eloop timeout event callback type
+ * @eloop_ctx: Registered callback context data (eloop_data)
+ * @user_ctx: Registered callback context data (user_data)
+ */
+typedef void (*eloop_timeout_handler)(void *eloop_ctx, void *user_ctx);
+
+/**
+ * eloop_signal_handler - eloop signal event callback type
+ * @sig: Signal number
+ * @signal_ctx: Registered callback context data (user_data from
+ * eloop_register_signal(), eloop_register_signal_terminate(), or
+ * eloop_register_signal_reconfig() call)
+ */
+typedef void (*eloop_signal_handler)(int sig, void *signal_ctx);
+
+/**
+ * eloop_init() - Initialize global event loop data
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function must be called before any other eloop_* function.
+ */
+int eloop_init(void);
+
+/**
+ * eloop_register_read_sock - Register handler for read events
+ * @sock: File descriptor number for the socket
+ * @handler: Callback function to be called when data is available for reading
+ * @eloop_data: Callback context data (eloop_ctx)
+ * @user_data: Callback context data (sock_ctx)
+ * Returns: 0 on success, -1 on failure
+ *
+ * Register a read socket notifier for the given file descriptor. The handler
+ * function will be called whenever data is available for reading from the
+ * socket. The handler function is responsible for clearing the event after
+ * having processed it in order to avoid eloop from calling the handler again
+ * for the same event.
+ */
+int eloop_register_read_sock(int sock, eloop_sock_handler handler,
+			     void *eloop_data, void *user_data);
+
+/**
+ * eloop_unregister_read_sock - Unregister handler for read events
+ * @sock: File descriptor number for the socket
+ *
+ * Unregister a read socket notifier that was previously registered with
+ * eloop_register_read_sock().
+ */
+void eloop_unregister_read_sock(int sock);
+
+/**
+ * eloop_register_sock - Register handler for socket events
+ * @sock: File descriptor number for the socket
+ * @type: Type of event to wait for
+ * @handler: Callback function to be called when the event is triggered
+ * @eloop_data: Callback context data (eloop_ctx)
+ * @user_data: Callback context data (sock_ctx)
+ * Returns: 0 on success, -1 on failure
+ *
+ * Register an event notifier for the given socket's file descriptor. The
+ * handler function will be called whenever the that event is triggered for the
+ * socket. The handler function is responsible for clearing the event after
+ * having processed it in order to avoid eloop from calling the handler again
+ * for the same event.
+ */
+int eloop_register_sock(int sock, eloop_event_type type,
+			eloop_sock_handler handler,
+			void *eloop_data, void *user_data);
+
+/**
+ * eloop_unregister_sock - Unregister handler for socket events
+ * @sock: File descriptor number for the socket
+ * @type: Type of event for which sock was registered
+ *
+ * Unregister a socket event notifier that was previously registered with
+ * eloop_register_sock().
+ */
+void eloop_unregister_sock(int sock, eloop_event_type type);
+
+/**
+ * eloop_register_event - Register handler for generic events
+ * @event: Event to wait (eloop implementation specific)
+ * @event_size: Size of event data
+ * @handler: Callback function to be called when event is triggered
+ * @eloop_data: Callback context data (eloop_data)
+ * @user_data: Callback context data (user_data)
+ * Returns: 0 on success, -1 on failure
+ *
+ * Register an event handler for the given event. This function is used to
+ * register eloop implementation specific events which are mainly targeted for
+ * operating system specific code (driver interface and l2_packet) since the
+ * portable code will not be able to use such an OS-specific call. The handler
+ * function will be called whenever the event is triggered. The handler
+ * function is responsible for clearing the event after having processed it in
+ * order to avoid eloop from calling the handler again for the same event.
+ *
+ * In case of Windows implementation (eloop_win.c), event pointer is of HANDLE
+ * type, i.e., void*. The callers are likely to have 'HANDLE h' type variable,
+ * and they would call this function with eloop_register_event(h, sizeof(h),
+ * ...).
+ */
+int eloop_register_event(void *event, size_t event_size,
+			 eloop_event_handler handler,
+			 void *eloop_data, void *user_data);
+
+/**
+ * eloop_unregister_event - Unregister handler for a generic event
+ * @event: Event to cancel (eloop implementation specific)
+ * @event_size: Size of event data
+ *
+ * Unregister a generic event notifier that was previously registered with
+ * eloop_register_event().
+ */
+void eloop_unregister_event(void *event, size_t event_size);
+
+/**
+ * eloop_register_timeout - Register timeout
+ * @secs: Number of seconds to the timeout
+ * @usecs: Number of microseconds to the timeout
+ * @handler: Callback function to be called when timeout occurs
+ * @eloop_data: Callback context data (eloop_ctx)
+ * @user_data: Callback context data (sock_ctx)
+ * Returns: 0 on success, -1 on failure
+ *
+ * Register a timeout that will cause the handler function to be called after
+ * given time.
+ */
+int eloop_register_timeout(unsigned int secs, unsigned int usecs,
+			   eloop_timeout_handler handler,
+			   void *eloop_data, void *user_data);
+
+/**
+ * eloop_cancel_timeout - Cancel timeouts
+ * @handler: Matching callback function
+ * @eloop_data: Matching eloop_data or %ELOOP_ALL_CTX to match all
+ * @user_data: Matching user_data or %ELOOP_ALL_CTX to match all
+ * Returns: Number of cancelled timeouts
+ *
+ * Cancel matching <handler,eloop_data,user_data> timeouts registered with
+ * eloop_register_timeout(). ELOOP_ALL_CTX can be used as a wildcard for
+ * cancelling all timeouts regardless of eloop_data/user_data.
+ */
+int eloop_cancel_timeout(eloop_timeout_handler handler,
+			 void *eloop_data, void *user_data);
+
+/**
+ * eloop_cancel_timeout_one - Cancel a single timeout
+ * @handler: Matching callback function
+ * @eloop_data: Matching eloop_data
+ * @user_data: Matching user_data
+ * @remaining: Time left on the cancelled timer
+ * Returns: Number of cancelled timeouts
+ *
+ * Cancel matching <handler,eloop_data,user_data> timeout registered with
+ * eloop_register_timeout() and return the remaining time left.
+ */
+int eloop_cancel_timeout_one(eloop_timeout_handler handler,
+			     void *eloop_data, void *user_data,
+			     struct os_reltime *remaining);
+
+/**
+ * eloop_is_timeout_registered - Check if a timeout is already registered
+ * @handler: Matching callback function
+ * @eloop_data: Matching eloop_data
+ * @user_data: Matching user_data
+ * Returns: 1 if the timeout is registered, 0 if the timeout is not registered
+ *
+ * Determine if a matching <handler,eloop_data,user_data> timeout is registered
+ * with eloop_register_timeout().
+ */
+int eloop_is_timeout_registered(eloop_timeout_handler handler,
+				void *eloop_data, void *user_data);
+
+/**
+ * eloop_deplete_timeout - Deplete a timeout that is already registered
+ * @req_secs: Requested number of seconds to the timeout
+ * @req_usecs: Requested number of microseconds to the timeout
+ * @handler: Matching callback function
+ * @eloop_data: Matching eloop_data
+ * @user_data: Matching user_data
+ * Returns: 1 if the timeout is depleted, 0 if no change is made, -1 if no
+ * timeout matched
+ *
+ * Find a registered matching <handler,eloop_data,user_data> timeout. If found,
+ * deplete the timeout if remaining time is more than the requested time.
+ */
+int eloop_deplete_timeout(unsigned int req_secs, unsigned int req_usecs,
+			  eloop_timeout_handler handler, void *eloop_data,
+			  void *user_data);
+
+/**
+ * eloop_replenish_timeout - Replenish a timeout that is already registered
+ * @req_secs: Requested number of seconds to the timeout
+ * @req_usecs: Requested number of microseconds to the timeout
+ * @handler: Matching callback function
+ * @eloop_data: Matching eloop_data
+ * @user_data: Matching user_data
+ * Returns: 1 if the timeout is replenished, 0 if no change is made, -1 if no
+ * timeout matched
+ *
+ * Find a registered matching <handler,eloop_data,user_data> timeout. If found,
+ * replenish the timeout if remaining time is less than the requested time.
+ */
+int eloop_replenish_timeout(unsigned int req_secs, unsigned int req_usecs,
+			    eloop_timeout_handler handler, void *eloop_data,
+			    void *user_data);
+
+/**
+ * eloop_register_signal - Register handler for signals
+ * @sig: Signal number (e.g., SIGHUP)
+ * @handler: Callback function to be called when the signal is received
+ * @user_data: Callback context data (signal_ctx)
+ * Returns: 0 on success, -1 on failure
+ *
+ * Register a callback function that will be called when a signal is received.
+ * The callback function is actually called only after the system signal
+ * handler has returned. This means that the normal limits for sighandlers
+ * (i.e., only "safe functions" allowed) do not apply for the registered
+ * callback.
+ */
+int eloop_register_signal(int sig, eloop_signal_handler handler,
+			  void *user_data);
+
+/**
+ * eloop_register_signal_terminate - Register handler for terminate signals
+ * @handler: Callback function to be called when the signal is received
+ * @user_data: Callback context data (signal_ctx)
+ * Returns: 0 on success, -1 on failure
+ *
+ * Register a callback function that will be called when a process termination
+ * signal is received. The callback function is actually called only after the
+ * system signal handler has returned. This means that the normal limits for
+ * sighandlers (i.e., only "safe functions" allowed) do not apply for the
+ * registered callback.
+ *
+ * This function is a more portable version of eloop_register_signal() since
+ * the knowledge of exact details of the signals is hidden in eloop
+ * implementation. In case of operating systems using signal(), this function
+ * registers handlers for SIGINT and SIGTERM.
+ */
+int eloop_register_signal_terminate(eloop_signal_handler handler,
+				    void *user_data);
+
+/**
+ * eloop_register_signal_reconfig - Register handler for reconfig signals
+ * @handler: Callback function to be called when the signal is received
+ * @user_data: Callback context data (signal_ctx)
+ * Returns: 0 on success, -1 on failure
+ *
+ * Register a callback function that will be called when a reconfiguration /
+ * hangup signal is received. The callback function is actually called only
+ * after the system signal handler has returned. This means that the normal
+ * limits for sighandlers (i.e., only "safe functions" allowed) do not apply
+ * for the registered callback.
+ *
+ * This function is a more portable version of eloop_register_signal() since
+ * the knowledge of exact details of the signals is hidden in eloop
+ * implementation. In case of operating systems using signal(), this function
+ * registers a handler for SIGHUP.
+ */
+int eloop_register_signal_reconfig(eloop_signal_handler handler,
+				   void *user_data);
+
+/**
+ * eloop_sock_requeue - Requeue sockets
+ *
+ * Requeue sockets after forking because some implementations require this,
+ * such as epoll and kqueue.
+ */
+int eloop_sock_requeue(void);
+
+/**
+ * eloop_run - Start the event loop
+ *
+ * Start the event loop and continue running as long as there are any
+ * registered event handlers. This function is run after event loop has been
+ * initialized with event_init() and one or more events have been registered.
+ */
+void eloop_run(void);
+
+/**
+ * eloop_terminate - Terminate event loop
+ *
+ * Terminate event loop even if there are registered events. This can be used
+ * to request the program to be terminated cleanly.
+ */
+void eloop_terminate(void);
+
+/**
+ * eloop_destroy - Free any resources allocated for the event loop
+ *
+ * After calling eloop_destroy(), other eloop_* functions must not be called
+ * before re-running eloop_init().
+ */
+void eloop_destroy(void);
+
+/**
+ * eloop_terminated - Check whether event loop has been terminated
+ * Returns: 1 = event loop terminate, 0 = event loop still running
+ *
+ * This function can be used to check whether eloop_terminate() has been called
+ * to request termination of the event loop. This is normally used to abort
+ * operations that may still be queued to be run when eloop_terminate() was
+ * called.
+ */
+int eloop_terminated(void);
+
+/**
+ * eloop_wait_for_read_sock - Wait for a single reader
+ * @sock: File descriptor number for the socket
+ *
+ * Do a blocking wait for a single read socket.
+ */
+void eloop_wait_for_read_sock(int sock);
+
+#endif /* ELOOP_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/eloop_win.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/eloop_win.c
new file mode 100644
index 0000000..74eaa33
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/eloop_win.c
@@ -0,0 +1,700 @@
+/*
+ * Event loop based on Windows events and WaitForMultipleObjects
+ * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <winsock2.h>
+
+#include "common.h"
+#include "list.h"
+#include "eloop.h"
+
+
+struct eloop_sock {
+	int sock;
+	void *eloop_data;
+	void *user_data;
+	eloop_sock_handler handler;
+	WSAEVENT event;
+};
+
+struct eloop_event {
+	void *eloop_data;
+	void *user_data;
+	eloop_event_handler handler;
+	HANDLE event;
+};
+
+struct eloop_timeout {
+	struct dl_list list;
+	struct os_reltime time;
+	void *eloop_data;
+	void *user_data;
+	eloop_timeout_handler handler;
+};
+
+struct eloop_signal {
+	int sig;
+	void *user_data;
+	eloop_signal_handler handler;
+	int signaled;
+};
+
+struct eloop_data {
+	int max_sock;
+	size_t reader_count;
+	struct eloop_sock *readers;
+
+	size_t event_count;
+	struct eloop_event *events;
+
+	struct dl_list timeout;
+
+	size_t signal_count;
+	struct eloop_signal *signals;
+	int signaled;
+	int pending_terminate;
+
+	int terminate;
+	int reader_table_changed;
+
+	struct eloop_signal term_signal;
+	HANDLE term_event;
+
+	HANDLE *handles;
+	size_t num_handles;
+};
+
+static struct eloop_data eloop;
+
+
+int eloop_init(void)
+{
+	os_memset(&eloop, 0, sizeof(eloop));
+	dl_list_init(&eloop.timeout);
+	eloop.num_handles = 1;
+	eloop.handles = os_malloc(eloop.num_handles *
+				  sizeof(eloop.handles[0]));
+	if (eloop.handles == NULL)
+		return -1;
+
+	eloop.term_event = CreateEvent(NULL, FALSE, FALSE, NULL);
+	if (eloop.term_event == NULL) {
+		printf("CreateEvent() failed: %d\n",
+		       (int) GetLastError());
+		os_free(eloop.handles);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int eloop_prepare_handles(void)
+{
+	HANDLE *n;
+
+	if (eloop.num_handles > eloop.reader_count + eloop.event_count + 8)
+		return 0;
+	n = os_realloc_array(eloop.handles, eloop.num_handles * 2,
+			     sizeof(eloop.handles[0]));
+	if (n == NULL)
+		return -1;
+	eloop.handles = n;
+	eloop.num_handles *= 2;
+	return 0;
+}
+
+
+int eloop_register_read_sock(int sock, eloop_sock_handler handler,
+			     void *eloop_data, void *user_data)
+{
+	WSAEVENT event;
+	struct eloop_sock *tmp;
+
+	if (eloop_prepare_handles())
+		return -1;
+
+	event = WSACreateEvent();
+	if (event == WSA_INVALID_EVENT) {
+		printf("WSACreateEvent() failed: %d\n", WSAGetLastError());
+		return -1;
+	}
+
+	if (WSAEventSelect(sock, event, FD_READ)) {
+		printf("WSAEventSelect() failed: %d\n", WSAGetLastError());
+		WSACloseEvent(event);
+		return -1;
+	}
+	tmp = os_realloc_array(eloop.readers, eloop.reader_count + 1,
+			       sizeof(struct eloop_sock));
+	if (tmp == NULL) {
+		WSAEventSelect(sock, event, 0);
+		WSACloseEvent(event);
+		return -1;
+	}
+
+	tmp[eloop.reader_count].sock = sock;
+	tmp[eloop.reader_count].eloop_data = eloop_data;
+	tmp[eloop.reader_count].user_data = user_data;
+	tmp[eloop.reader_count].handler = handler;
+	tmp[eloop.reader_count].event = event;
+	eloop.reader_count++;
+	eloop.readers = tmp;
+	if (sock > eloop.max_sock)
+		eloop.max_sock = sock;
+	eloop.reader_table_changed = 1;
+
+	return 0;
+}
+
+
+void eloop_unregister_read_sock(int sock)
+{
+	size_t i;
+
+	if (eloop.readers == NULL || eloop.reader_count == 0)
+		return;
+
+	for (i = 0; i < eloop.reader_count; i++) {
+		if (eloop.readers[i].sock == sock)
+			break;
+	}
+	if (i == eloop.reader_count)
+		return;
+
+	WSAEventSelect(eloop.readers[i].sock, eloop.readers[i].event, 0);
+	WSACloseEvent(eloop.readers[i].event);
+
+	if (i != eloop.reader_count - 1) {
+		os_memmove(&eloop.readers[i], &eloop.readers[i + 1],
+			   (eloop.reader_count - i - 1) *
+			   sizeof(struct eloop_sock));
+	}
+	eloop.reader_count--;
+	eloop.reader_table_changed = 1;
+}
+
+
+int eloop_register_event(void *event, size_t event_size,
+			 eloop_event_handler handler,
+			 void *eloop_data, void *user_data)
+{
+	struct eloop_event *tmp;
+	HANDLE h = event;
+
+	if (event_size != sizeof(HANDLE) || h == INVALID_HANDLE_VALUE)
+		return -1;
+
+	if (eloop_prepare_handles())
+		return -1;
+
+	tmp = os_realloc_array(eloop.events, eloop.event_count + 1,
+			       sizeof(struct eloop_event));
+	if (tmp == NULL)
+		return -1;
+
+	tmp[eloop.event_count].eloop_data = eloop_data;
+	tmp[eloop.event_count].user_data = user_data;
+	tmp[eloop.event_count].handler = handler;
+	tmp[eloop.event_count].event = h;
+	eloop.event_count++;
+	eloop.events = tmp;
+
+	return 0;
+}
+
+
+void eloop_unregister_event(void *event, size_t event_size)
+{
+	size_t i;
+	HANDLE h = event;
+
+	if (eloop.events == NULL || eloop.event_count == 0 ||
+	    event_size != sizeof(HANDLE))
+		return;
+
+	for (i = 0; i < eloop.event_count; i++) {
+		if (eloop.events[i].event == h)
+			break;
+	}
+	if (i == eloop.event_count)
+		return;
+
+	if (i != eloop.event_count - 1) {
+		os_memmove(&eloop.events[i], &eloop.events[i + 1],
+			   (eloop.event_count - i - 1) *
+			   sizeof(struct eloop_event));
+	}
+	eloop.event_count--;
+}
+
+
+int eloop_register_timeout(unsigned int secs, unsigned int usecs,
+			   eloop_timeout_handler handler,
+			   void *eloop_data, void *user_data)
+{
+	struct eloop_timeout *timeout, *tmp;
+	os_time_t now_sec;
+
+	timeout = os_zalloc(sizeof(*timeout));
+	if (timeout == NULL)
+		return -1;
+	if (os_get_reltime(&timeout->time) < 0) {
+		os_free(timeout);
+		return -1;
+	}
+	now_sec = timeout->time.sec;
+	timeout->time.sec += secs;
+	if (timeout->time.sec < now_sec) {
+		/*
+		 * Integer overflow - assume long enough timeout to be assumed
+		 * to be infinite, i.e., the timeout would never happen.
+		 */
+		wpa_printf(MSG_DEBUG, "ELOOP: Too long timeout (secs=%u) to "
+			   "ever happen - ignore it", secs);
+		os_free(timeout);
+		return 0;
+	}
+	timeout->time.usec += usecs;
+	while (timeout->time.usec >= 1000000) {
+		timeout->time.sec++;
+		timeout->time.usec -= 1000000;
+	}
+	timeout->eloop_data = eloop_data;
+	timeout->user_data = user_data;
+	timeout->handler = handler;
+
+	/* Maintain timeouts in order of increasing time */
+	dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) {
+		if (os_reltime_before(&timeout->time, &tmp->time)) {
+			dl_list_add(tmp->list.prev, &timeout->list);
+			return 0;
+		}
+	}
+	dl_list_add_tail(&eloop.timeout, &timeout->list);
+
+	return 0;
+}
+
+
+static void eloop_remove_timeout(struct eloop_timeout *timeout)
+{
+	dl_list_del(&timeout->list);
+	os_free(timeout);
+}
+
+
+int eloop_cancel_timeout(eloop_timeout_handler handler,
+			 void *eloop_data, void *user_data)
+{
+	struct eloop_timeout *timeout, *prev;
+	int removed = 0;
+
+	dl_list_for_each_safe(timeout, prev, &eloop.timeout,
+			      struct eloop_timeout, list) {
+		if (timeout->handler == handler &&
+		    (timeout->eloop_data == eloop_data ||
+		     eloop_data == ELOOP_ALL_CTX) &&
+		    (timeout->user_data == user_data ||
+		     user_data == ELOOP_ALL_CTX)) {
+			eloop_remove_timeout(timeout);
+			removed++;
+		}
+	}
+
+	return removed;
+}
+
+
+int eloop_cancel_timeout_one(eloop_timeout_handler handler,
+			     void *eloop_data, void *user_data,
+			     struct os_reltime *remaining)
+{
+	struct eloop_timeout *timeout, *prev;
+	int removed = 0;
+	struct os_reltime now;
+
+	os_get_reltime(&now);
+	remaining->sec = remaining->usec = 0;
+
+	dl_list_for_each_safe(timeout, prev, &eloop.timeout,
+			      struct eloop_timeout, list) {
+		if (timeout->handler == handler &&
+		    (timeout->eloop_data == eloop_data) &&
+		    (timeout->user_data == user_data)) {
+			removed = 1;
+			if (os_reltime_before(&now, &timeout->time))
+				os_reltime_sub(&timeout->time, &now, remaining);
+			eloop_remove_timeout(timeout);
+			break;
+		}
+	}
+	return removed;
+}
+
+
+int eloop_is_timeout_registered(eloop_timeout_handler handler,
+				void *eloop_data, void *user_data)
+{
+	struct eloop_timeout *tmp;
+
+	dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) {
+		if (tmp->handler == handler &&
+		    tmp->eloop_data == eloop_data &&
+		    tmp->user_data == user_data)
+			return 1;
+	}
+
+	return 0;
+}
+
+
+int eloop_deplete_timeout(unsigned int req_secs, unsigned int req_usecs,
+			  eloop_timeout_handler handler, void *eloop_data,
+			  void *user_data)
+{
+	struct os_reltime now, requested, remaining;
+	struct eloop_timeout *tmp;
+
+	dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) {
+		if (tmp->handler == handler &&
+		    tmp->eloop_data == eloop_data &&
+		    tmp->user_data == user_data) {
+			requested.sec = req_secs;
+			requested.usec = req_usecs;
+			os_get_reltime(&now);
+			os_reltime_sub(&tmp->time, &now, &remaining);
+			if (os_reltime_before(&requested, &remaining)) {
+				eloop_cancel_timeout(handler, eloop_data,
+						     user_data);
+				eloop_register_timeout(requested.sec,
+						       requested.usec,
+						       handler, eloop_data,
+						       user_data);
+				return 1;
+			}
+			return 0;
+		}
+	}
+
+	return -1;
+}
+
+
+int eloop_replenish_timeout(unsigned int req_secs, unsigned int req_usecs,
+			    eloop_timeout_handler handler, void *eloop_data,
+			    void *user_data)
+{
+	struct os_reltime now, requested, remaining;
+	struct eloop_timeout *tmp;
+
+	dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) {
+		if (tmp->handler == handler &&
+		    tmp->eloop_data == eloop_data &&
+		    tmp->user_data == user_data) {
+			requested.sec = req_secs;
+			requested.usec = req_usecs;
+			os_get_reltime(&now);
+			os_reltime_sub(&tmp->time, &now, &remaining);
+			if (os_reltime_before(&remaining, &requested)) {
+				eloop_cancel_timeout(handler, eloop_data,
+						     user_data);
+				eloop_register_timeout(requested.sec,
+						       requested.usec,
+						       handler, eloop_data,
+						       user_data);
+				return 1;
+			}
+			return 0;
+		}
+	}
+
+	return -1;
+}
+
+
+/* TODO: replace with suitable signal handler */
+#if 0
+static void eloop_handle_signal(int sig)
+{
+	size_t i;
+
+	eloop.signaled++;
+	for (i = 0; i < eloop.signal_count; i++) {
+		if (eloop.signals[i].sig == sig) {
+			eloop.signals[i].signaled++;
+			break;
+		}
+	}
+}
+#endif
+
+
+static void eloop_process_pending_signals(void)
+{
+	size_t i;
+
+	if (eloop.signaled == 0)
+		return;
+	eloop.signaled = 0;
+
+	if (eloop.pending_terminate) {
+		eloop.pending_terminate = 0;
+	}
+
+	for (i = 0; i < eloop.signal_count; i++) {
+		if (eloop.signals[i].signaled) {
+			eloop.signals[i].signaled = 0;
+			eloop.signals[i].handler(eloop.signals[i].sig,
+						 eloop.signals[i].user_data);
+		}
+	}
+
+	if (eloop.term_signal.signaled) {
+		eloop.term_signal.signaled = 0;
+		eloop.term_signal.handler(eloop.term_signal.sig,
+					  eloop.term_signal.user_data);
+	}
+}
+
+
+int eloop_register_signal(int sig, eloop_signal_handler handler,
+			  void *user_data)
+{
+	struct eloop_signal *tmp;
+
+	tmp = os_realloc_array(eloop.signals, eloop.signal_count + 1,
+			       sizeof(struct eloop_signal));
+	if (tmp == NULL)
+		return -1;
+
+	tmp[eloop.signal_count].sig = sig;
+	tmp[eloop.signal_count].user_data = user_data;
+	tmp[eloop.signal_count].handler = handler;
+	tmp[eloop.signal_count].signaled = 0;
+	eloop.signal_count++;
+	eloop.signals = tmp;
+
+	/* TODO: register signal handler */
+
+	return 0;
+}
+
+
+#ifndef _WIN32_WCE
+static BOOL eloop_handle_console_ctrl(DWORD type)
+{
+	switch (type) {
+	case CTRL_C_EVENT:
+	case CTRL_BREAK_EVENT:
+		eloop.signaled++;
+		eloop.term_signal.signaled++;
+		SetEvent(eloop.term_event);
+		return TRUE;
+	default:
+		return FALSE;
+	}
+}
+#endif /* _WIN32_WCE */
+
+
+int eloop_register_signal_terminate(eloop_signal_handler handler,
+				    void *user_data)
+{
+#ifndef _WIN32_WCE
+	if (SetConsoleCtrlHandler((PHANDLER_ROUTINE) eloop_handle_console_ctrl,
+				  TRUE) == 0) {
+		printf("SetConsoleCtrlHandler() failed: %d\n",
+		       (int) GetLastError());
+		return -1;
+	}
+#endif /* _WIN32_WCE */
+
+	eloop.term_signal.handler = handler;
+	eloop.term_signal.user_data = user_data;
+
+	return 0;
+}
+
+
+int eloop_register_signal_reconfig(eloop_signal_handler handler,
+				   void *user_data)
+{
+	/* TODO */
+	return 0;
+}
+
+
+void eloop_run(void)
+{
+	struct os_reltime tv, now;
+	DWORD count, ret, timeout_val, err;
+	size_t i;
+
+	while (!eloop.terminate &&
+	       (!dl_list_empty(&eloop.timeout) || eloop.reader_count > 0 ||
+		eloop.event_count > 0)) {
+		struct eloop_timeout *timeout;
+		tv.sec = tv.usec = 0;
+		timeout = dl_list_first(&eloop.timeout, struct eloop_timeout,
+					list);
+		if (timeout) {
+			os_get_reltime(&now);
+			if (os_reltime_before(&now, &timeout->time))
+				os_reltime_sub(&timeout->time, &now, &tv);
+		}
+
+		count = 0;
+		for (i = 0; i < eloop.event_count; i++)
+			eloop.handles[count++] = eloop.events[i].event;
+
+		for (i = 0; i < eloop.reader_count; i++)
+			eloop.handles[count++] = eloop.readers[i].event;
+
+		if (eloop.term_event)
+			eloop.handles[count++] = eloop.term_event;
+
+		if (timeout)
+			timeout_val = tv.sec * 1000 + tv.usec / 1000;
+		else
+			timeout_val = INFINITE;
+
+		if (count > MAXIMUM_WAIT_OBJECTS) {
+			printf("WaitForMultipleObjects: Too many events: "
+			       "%d > %d (ignoring extra events)\n",
+			       (int) count, MAXIMUM_WAIT_OBJECTS);
+			count = MAXIMUM_WAIT_OBJECTS;
+		}
+#ifdef _WIN32_WCE
+		ret = WaitForMultipleObjects(count, eloop.handles, FALSE,
+					     timeout_val);
+#else /* _WIN32_WCE */
+		ret = WaitForMultipleObjectsEx(count, eloop.handles, FALSE,
+					       timeout_val, TRUE);
+#endif /* _WIN32_WCE */
+		err = GetLastError();
+
+		eloop_process_pending_signals();
+
+		/* check if some registered timeouts have occurred */
+		timeout = dl_list_first(&eloop.timeout, struct eloop_timeout,
+					list);
+		if (timeout) {
+			os_get_reltime(&now);
+			if (!os_reltime_before(&now, &timeout->time)) {
+				void *eloop_data = timeout->eloop_data;
+				void *user_data = timeout->user_data;
+				eloop_timeout_handler handler =
+					timeout->handler;
+				eloop_remove_timeout(timeout);
+				handler(eloop_data, user_data);
+			}
+
+		}
+
+		if (ret == WAIT_FAILED) {
+			printf("WaitForMultipleObjects(count=%d) failed: %d\n",
+			       (int) count, (int) err);
+			os_sleep(1, 0);
+			continue;
+		}
+
+#ifndef _WIN32_WCE
+		if (ret == WAIT_IO_COMPLETION)
+			continue;
+#endif /* _WIN32_WCE */
+
+		if (ret == WAIT_TIMEOUT)
+			continue;
+
+		while (ret >= WAIT_OBJECT_0 &&
+		       ret < WAIT_OBJECT_0 + eloop.event_count) {
+			eloop.events[ret].handler(
+				eloop.events[ret].eloop_data,
+				eloop.events[ret].user_data);
+			ret = WaitForMultipleObjects(eloop.event_count,
+						     eloop.handles, FALSE, 0);
+		}
+
+		eloop.reader_table_changed = 0;
+		for (i = 0; i < eloop.reader_count; i++) {
+			WSANETWORKEVENTS events;
+			if (WSAEnumNetworkEvents(eloop.readers[i].sock,
+						 eloop.readers[i].event,
+						 &events) == 0 &&
+			    (events.lNetworkEvents & FD_READ)) {
+				eloop.readers[i].handler(
+					eloop.readers[i].sock,
+					eloop.readers[i].eloop_data,
+					eloop.readers[i].user_data);
+				if (eloop.reader_table_changed)
+					break;
+			}
+		}
+	}
+}
+
+
+void eloop_terminate(void)
+{
+	eloop.terminate = 1;
+	SetEvent(eloop.term_event);
+}
+
+
+void eloop_destroy(void)
+{
+	struct eloop_timeout *timeout, *prev;
+
+	dl_list_for_each_safe(timeout, prev, &eloop.timeout,
+			      struct eloop_timeout, list) {
+		eloop_remove_timeout(timeout);
+	}
+	os_free(eloop.readers);
+	os_free(eloop.signals);
+	if (eloop.term_event)
+		CloseHandle(eloop.term_event);
+	os_free(eloop.handles);
+	eloop.handles = NULL;
+	os_free(eloop.events);
+	eloop.events = NULL;
+}
+
+
+int eloop_terminated(void)
+{
+	return eloop.terminate;
+}
+
+
+void eloop_wait_for_read_sock(int sock)
+{
+	WSAEVENT event;
+
+	event = WSACreateEvent();
+	if (event == WSA_INVALID_EVENT) {
+		printf("WSACreateEvent() failed: %d\n", WSAGetLastError());
+		return;
+	}
+
+	if (WSAEventSelect(sock, event, FD_READ)) {
+		printf("WSAEventSelect() failed: %d\n", WSAGetLastError());
+		WSACloseEvent(event);
+		return ;
+	}
+
+	WaitForSingleObject(event, INFINITE);
+	WSAEventSelect(sock, event, 0);
+	WSACloseEvent(event);
+}
+
+
+int eloop_sock_requeue(void)
+{
+	return 0;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/ext_password.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/ext_password.c
new file mode 100644
index 0000000..cbf92de
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/ext_password.c
@@ -0,0 +1,115 @@
+/*
+ * External password backend
+ * Copyright (c) 2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#ifdef __linux__
+#include <sys/mman.h>
+#endif /* __linux__ */
+
+#include "common.h"
+#include "ext_password_i.h"
+
+
+static const struct ext_password_backend *backends[] = {
+#ifdef CONFIG_EXT_PASSWORD_TEST
+	&ext_password_test,
+#endif /* CONFIG_EXT_PASSWORD_TEST */
+#ifdef CONFIG_EXT_PASSWORD_FILE
+	&ext_password_file,
+#endif /* CONFIG_EXT_PASSWORD_FILE */
+	NULL
+};
+
+struct ext_password_data {
+	const struct ext_password_backend *backend;
+	void *priv;
+};
+
+
+struct ext_password_data * ext_password_init(const char *backend,
+					     const char *params)
+{
+	struct ext_password_data *data;
+	int i;
+
+	data = os_zalloc(sizeof(*data));
+	if (data == NULL)
+		return NULL;
+
+	for (i = 0; backends[i]; i++) {
+		if (os_strcmp(backends[i]->name, backend) == 0) {
+			data->backend = backends[i];
+			break;
+		}
+	}
+
+	if (!data->backend) {
+		os_free(data);
+		return NULL;
+	}
+
+	data->priv = data->backend->init(params);
+	if (data->priv == NULL) {
+		os_free(data);
+		return NULL;
+	}
+
+	return data;
+}
+
+
+void ext_password_deinit(struct ext_password_data *data)
+{
+	if (data && data->backend && data->priv)
+		data->backend->deinit(data->priv);
+	os_free(data);
+}
+
+
+struct wpabuf * ext_password_get(struct ext_password_data *data,
+				 const char *name)
+{
+	if (data == NULL)
+		return NULL;
+	return data->backend->get(data->priv, name);
+}
+
+
+struct wpabuf * ext_password_alloc(size_t len)
+{
+	struct wpabuf *buf;
+
+	buf = wpabuf_alloc(len);
+	if (buf == NULL)
+		return NULL;
+
+#ifdef __linux__
+	if (mlock(wpabuf_head(buf), wpabuf_len(buf)) < 0) {
+		wpa_printf(MSG_ERROR, "EXT PW: mlock failed: %s",
+			   strerror(errno));
+	}
+#endif /* __linux__ */
+
+	return buf;
+}
+
+
+void ext_password_free(struct wpabuf *pw)
+{
+	if (pw == NULL)
+		return;
+	os_memset(wpabuf_mhead(pw), 0, wpabuf_len(pw));
+#ifdef __linux__
+	if (munlock(wpabuf_head(pw), wpabuf_len(pw)) < 0) {
+		wpa_printf(MSG_ERROR, "EXT PW: munlock failed: %s",
+			   strerror(errno));
+	}
+#endif /* __linux__ */
+	wpabuf_free(pw);
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/ext_password.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/ext_password.h
new file mode 100644
index 0000000..e3e46ea
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/ext_password.h
@@ -0,0 +1,33 @@
+/*
+ * External password backend
+ * Copyright (c) 2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef EXT_PASSWORD_H
+#define EXT_PASSWORD_H
+
+struct ext_password_data;
+
+#ifdef CONFIG_EXT_PASSWORD
+
+struct ext_password_data * ext_password_init(const char *backend,
+					     const char *params);
+void ext_password_deinit(struct ext_password_data *data);
+
+struct wpabuf * ext_password_get(struct ext_password_data *data,
+				 const char *name);
+void ext_password_free(struct wpabuf *pw);
+
+#else /* CONFIG_EXT_PASSWORD */
+
+#define ext_password_init(b, p) ((void *) 1)
+#define ext_password_deinit(d) do { } while (0)
+#define ext_password_get(d, n) (NULL)
+#define ext_password_free(p) do { } while (0)
+
+#endif /* CONFIG_EXT_PASSWORD */
+
+#endif /* EXT_PASSWORD_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/ext_password_file.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/ext_password_file.c
new file mode 100644
index 0000000..4bb0095
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/ext_password_file.c
@@ -0,0 +1,136 @@
+/*
+ * External backend for file-backed passwords
+ * Copyright (c) 2021, Patrick Steinhardt <ps@pks.im>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "utils/common.h"
+#include "utils/config.h"
+#include "ext_password_i.h"
+
+
+/**
+ * Data structure for the file-backed password backend.
+ */
+struct ext_password_file_data {
+	char *path; /* path of the password file */
+};
+
+
+/**
+ * ext_password_file_init - Initialize file-backed password backend
+ * @params: Parameters passed by the user.
+ * Returns: Pointer to the initialized backend.
+ *
+ * This function initializes a new file-backed password backend. The user is
+ * expected to initialize this backend with the parameters being the path of
+ * the file that contains the passwords.
+ */
+static void * ext_password_file_init(const char *params)
+{
+	struct ext_password_file_data *data;
+
+	if (!params) {
+		wpa_printf(MSG_ERROR, "EXT PW FILE: no path given");
+		return NULL;
+	}
+
+	data = os_zalloc(sizeof(*data));
+	if (!data)
+		return NULL;
+
+	data->path = os_strdup(params);
+	if (!data->path) {
+		os_free(data);
+		return NULL;
+	}
+
+	return data;
+}
+
+
+/**
+ * ext_password_file_deinit - Deinitialize file-backed password backend
+ * @ctx: The file-backed password backend
+ *
+ * This function frees all data associated with the file-backed password
+ * backend.
+ */
+static void ext_password_file_deinit(void *ctx)
+{
+	struct ext_password_file_data *data = ctx;
+
+	str_clear_free(data->path);
+	os_free(data);
+}
+
+/**
+ * ext_password_file_get - Retrieve password from the file-backed password backend
+ * @ctx: The file-backed password backend
+ * @name: Name of the password to retrieve
+ * Returns: Buffer containing the password if one was found or %NULL.
+ *
+ * This function tries to find a password identified by name in the password
+ * file. The password is expected to be stored in `NAME=PASSWORD` format.
+ * Comments and empty lines in the file are ignored. Invalid lines will cause
+ * an error message, but will not cause the function to fail.
+ */
+static struct wpabuf * ext_password_file_get(void *ctx, const char *name)
+{
+	struct ext_password_file_data *data = ctx;
+	struct wpabuf *password = NULL;
+	char buf[512], *pos;
+	int line = 0;
+	FILE *f;
+
+	f = fopen(data->path, "r");
+	if (!f) {
+		wpa_printf(MSG_ERROR,
+			   "EXT PW FILE: could not open file '%s': %s",
+			   data->path, strerror(errno));
+		return NULL;
+	}
+
+	wpa_printf(MSG_DEBUG, "EXT PW FILE: get(%s)", name);
+
+	while (wpa_config_get_line(buf, sizeof(buf), f, &line, &pos)) {
+		char *sep = os_strchr(pos, '=');
+
+		if (!sep) {
+			wpa_printf(MSG_ERROR, "Invalid password line %d.",
+				   line);
+			continue;
+		}
+
+		if (!sep[1]) {
+			wpa_printf(MSG_ERROR, "No password for line %d.", line);
+			continue;
+
+		}
+
+		if (os_strncmp(name, pos, sep - pos) != 0)
+			continue;
+
+		password = wpabuf_alloc_copy(sep + 1, os_strlen(sep + 1));
+		goto done;
+	}
+
+	wpa_printf(MSG_ERROR, "Password for '%s' was not found.", name);
+
+done:
+	forced_memzero(buf, sizeof(buf));
+	fclose(f);
+	return password;
+}
+
+
+const struct ext_password_backend ext_password_file = {
+	.name = "file",
+	.init = ext_password_file_init,
+	.deinit = ext_password_file_deinit,
+	.get = ext_password_file_get,
+};
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/ext_password_i.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/ext_password_i.h
new file mode 100644
index 0000000..872ccd1
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/ext_password_i.h
@@ -0,0 +1,33 @@
+/*
+ * External password backend - internal definitions
+ * Copyright (c) 2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef EXT_PASSWORD_I_H
+#define EXT_PASSWORD_I_H
+
+#include "ext_password.h"
+
+struct ext_password_backend {
+	const char *name;
+	void * (*init)(const char *params);
+	void (*deinit)(void *ctx);
+	struct wpabuf * (*get)(void *ctx, const char *name);
+};
+
+struct wpabuf * ext_password_alloc(size_t len);
+
+/* Available ext_password backends */
+
+#ifdef CONFIG_EXT_PASSWORD_TEST
+extern const struct ext_password_backend ext_password_test;
+#endif /* CONFIG_EXT_PASSWORD_TEST */
+
+#ifdef CONFIG_EXT_PASSWORD_FILE
+extern const struct ext_password_backend ext_password_file;
+#endif /* CONFIG_EXT_PASSWORD_FILE */
+
+#endif /* EXT_PASSWORD_I_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/ext_password_test.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/ext_password_test.c
new file mode 100644
index 0000000..b3a4552
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/ext_password_test.c
@@ -0,0 +1,90 @@
+/*
+ * External password backend
+ * Copyright (c) 2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "ext_password_i.h"
+
+
+struct ext_password_test_data {
+	char *params;
+};
+
+
+static void * ext_password_test_init(const char *params)
+{
+	struct ext_password_test_data *data;
+
+	data = os_zalloc(sizeof(*data));
+	if (data == NULL)
+		return NULL;
+
+	if (params)
+		data->params = os_strdup(params);
+
+	return data;
+}
+
+
+static void ext_password_test_deinit(void *ctx)
+{
+	struct ext_password_test_data *data = ctx;
+
+	str_clear_free(data->params);
+	os_free(data);
+}
+
+
+static struct wpabuf * ext_password_test_get(void *ctx, const char *name)
+{
+	struct ext_password_test_data *data = ctx;
+	char *pos, *pos2;
+	size_t nlen;
+
+	wpa_printf(MSG_DEBUG, "EXT PW TEST: get(%s)", name);
+
+	pos = data->params;
+	if (pos == NULL)
+		return NULL;
+	nlen = os_strlen(name);
+
+	while (pos && *pos) {
+		if (os_strncmp(pos, name, nlen) == 0 && pos[nlen] == '=') {
+			struct wpabuf *buf;
+			pos += nlen + 1;
+			pos2 = pos;
+			while (*pos2 != '|' && *pos2 != '\0')
+				pos2++;
+			buf = ext_password_alloc(pos2 - pos);
+			if (buf == NULL)
+				return NULL;
+			wpabuf_put_data(buf, pos, pos2 - pos);
+			wpa_hexdump_ascii_key(MSG_DEBUG, "EXT PW TEST: value",
+					      wpabuf_head(buf),
+					      wpabuf_len(buf));
+			return buf;
+		}
+
+		pos = os_strchr(pos + 1, '|');
+		if (pos)
+			pos++;
+	}
+
+	wpa_printf(MSG_DEBUG, "EXT PW TEST: get(%s) - not found", name);
+
+	return NULL;
+}
+
+
+const struct ext_password_backend ext_password_test = {
+	.name = "test",
+	.init = ext_password_test_init,
+	.deinit = ext_password_test_deinit,
+	.get = ext_password_test_get,
+};
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/http-utils.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/http-utils.h
new file mode 100644
index 0000000..23e9ecd
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/http-utils.h
@@ -0,0 +1,64 @@
+/*
+ * HTTP wrapper
+ * Copyright (c) 2012-2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef HTTP_UTILS_H
+#define HTTP_UTILS_H
+
+struct http_ctx;
+
+struct http_othername {
+	char *oid;
+	u8 *data;
+	size_t len;
+};
+
+#define HTTP_MAX_CERT_LOGO_HASH 32
+
+struct http_logo {
+	char *alg_oid;
+	u8 *hash;
+	size_t hash_len;
+	char *uri;
+};
+
+struct http_cert {
+	char **dnsname;
+	size_t num_dnsname;
+	struct http_othername *othername;
+	size_t num_othername;
+	struct http_logo *logo;
+	size_t num_logo;
+	const char *url;
+};
+
+int soap_init_client(struct http_ctx *ctx, const char *address,
+		     const char *ca_fname, const char *username,
+		     const char *password, const char *client_cert,
+		     const char *client_key);
+int soap_reinit_client(struct http_ctx *ctx);
+xml_node_t * soap_send_receive(struct http_ctx *ctx, xml_node_t *node);
+
+struct http_ctx * http_init_ctx(void *upper_ctx, struct xml_node_ctx *xml_ctx);
+void http_ocsp_set(struct http_ctx *ctx, int val);
+void http_deinit_ctx(struct http_ctx *ctx);
+
+int http_download_file(struct http_ctx *ctx, const char *url,
+		       const char *fname, const char *ca_fname);
+char * http_post(struct http_ctx *ctx, const char *url, const char *data,
+		 const char *content_type, const char *ext_hdr,
+		 const char *ca_fname,
+		 const char *username, const char *password,
+		 const char *client_cert, const char *client_key,
+		 size_t *resp_len);
+void http_set_cert_cb(struct http_ctx *ctx,
+		      int (*cb)(void *ctx, struct http_cert *cert),
+		      void *cb_ctx);
+const char * http_get_err(struct http_ctx *ctx);
+void http_parse_x509_certificate(struct http_ctx *ctx, const char *fname);
+
+#endif /* HTTP_UTILS_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/http_curl.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/http_curl.c
new file mode 100644
index 0000000..77d5b35
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/http_curl.c
@@ -0,0 +1,1741 @@
+/*
+ * HTTP wrapper for libcurl
+ * Copyright (c) 2012-2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <curl/curl.h>
+#ifdef EAP_TLS_OPENSSL
+#include <openssl/ssl.h>
+#include <openssl/asn1.h>
+#include <openssl/asn1t.h>
+#include <openssl/x509v3.h>
+
+#ifdef SSL_set_tlsext_status_type
+#ifndef OPENSSL_NO_TLSEXT
+#define HAVE_OCSP
+#include <openssl/err.h>
+#include <openssl/ocsp.h>
+#endif /* OPENSSL_NO_TLSEXT */
+#endif /* SSL_set_tlsext_status_type */
+#endif /* EAP_TLS_OPENSSL */
+
+#include "common.h"
+#include "xml-utils.h"
+#include "http-utils.h"
+#ifdef EAP_TLS_OPENSSL
+#include "crypto/tls_openssl.h"
+#endif /* EAP_TLS_OPENSSL */
+
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+static const unsigned char * ASN1_STRING_get0_data(const ASN1_STRING *x)
+{
+	return ASN1_STRING_data((ASN1_STRING *) x);
+}
+#endif /* OpenSSL < 1.1.0 */
+
+
+struct http_ctx {
+	void *ctx;
+	struct xml_node_ctx *xml;
+	CURL *curl;
+	struct curl_slist *curl_hdr;
+	char *svc_address;
+	char *svc_ca_fname;
+	char *svc_username;
+	char *svc_password;
+	char *svc_client_cert;
+	char *svc_client_key;
+	char *curl_buf;
+	size_t curl_buf_len;
+
+	int (*cert_cb)(void *ctx, struct http_cert *cert);
+	void *cert_cb_ctx;
+
+	enum {
+		NO_OCSP, OPTIONAL_OCSP, MANDATORY_OCSP
+	} ocsp;
+	X509 *peer_cert;
+	X509 *peer_issuer;
+	X509 *peer_issuer_issuer;
+
+	const char *last_err;
+	const char *url;
+};
+
+
+static void clear_curl(struct http_ctx *ctx)
+{
+	if (ctx->curl) {
+		curl_easy_cleanup(ctx->curl);
+		ctx->curl = NULL;
+	}
+	if (ctx->curl_hdr) {
+		curl_slist_free_all(ctx->curl_hdr);
+		ctx->curl_hdr = NULL;
+	}
+}
+
+
+static void clone_str(char **dst, const char *src)
+{
+	os_free(*dst);
+	if (src)
+		*dst = os_strdup(src);
+	else
+		*dst = NULL;
+}
+
+
+static void debug_dump(struct http_ctx *ctx, const char *title,
+		       const char *buf, size_t len)
+{
+	char *txt;
+	size_t i;
+
+	for (i = 0; i < len; i++) {
+		if (buf[i] < 32 && buf[i] != '\t' && buf[i] != '\n' &&
+		    buf[i] != '\r') {
+			wpa_hexdump_ascii(MSG_MSGDUMP, title, buf, len);
+			return;
+		}
+	}
+
+	txt = os_malloc(len + 1);
+	if (txt == NULL)
+		return;
+	os_memcpy(txt, buf, len);
+	txt[len] = '\0';
+	while (len > 0) {
+		len--;
+		if (txt[len] == '\n' || txt[len] == '\r')
+			txt[len] = '\0';
+		else
+			break;
+	}
+	wpa_printf(MSG_MSGDUMP, "%s[%s]", title, txt);
+	os_free(txt);
+}
+
+
+static int curl_cb_debug(CURL *curl, curl_infotype info, char *buf, size_t len,
+			 void *userdata)
+{
+	struct http_ctx *ctx = userdata;
+	switch (info) {
+	case CURLINFO_TEXT:
+		debug_dump(ctx, "CURLINFO_TEXT", buf, len);
+		break;
+	case CURLINFO_HEADER_IN:
+		debug_dump(ctx, "CURLINFO_HEADER_IN", buf, len);
+		break;
+	case CURLINFO_HEADER_OUT:
+		debug_dump(ctx, "CURLINFO_HEADER_OUT", buf, len);
+		break;
+	case CURLINFO_DATA_IN:
+		debug_dump(ctx, "CURLINFO_DATA_IN", buf, len);
+		break;
+	case CURLINFO_DATA_OUT:
+		debug_dump(ctx, "CURLINFO_DATA_OUT", buf, len);
+		break;
+	case CURLINFO_SSL_DATA_IN:
+		wpa_printf(MSG_DEBUG, "debug - CURLINFO_SSL_DATA_IN - %d",
+			   (int) len);
+		break;
+	case CURLINFO_SSL_DATA_OUT:
+		wpa_printf(MSG_DEBUG, "debug - CURLINFO_SSL_DATA_OUT - %d",
+			   (int) len);
+		break;
+	case CURLINFO_END:
+		wpa_printf(MSG_DEBUG, "debug - CURLINFO_END - %d",
+			   (int) len);
+		break;
+	}
+	return 0;
+}
+
+
+static size_t curl_cb_write(void *ptr, size_t size, size_t nmemb,
+			    void *userdata)
+{
+	struct http_ctx *ctx = userdata;
+	char *n;
+	n = os_realloc(ctx->curl_buf, ctx->curl_buf_len + size * nmemb + 1);
+	if (n == NULL)
+		return 0;
+	ctx->curl_buf = n;
+	os_memcpy(n + ctx->curl_buf_len, ptr, size * nmemb);
+	n[ctx->curl_buf_len + size * nmemb] = '\0';
+	ctx->curl_buf_len += size * nmemb;
+	return size * nmemb;
+}
+
+
+#ifdef EAP_TLS_OPENSSL
+
+static void debug_dump_cert(const char *title, X509 *cert)
+{
+	BIO *out;
+	char *txt;
+	size_t rlen;
+
+	out = BIO_new(BIO_s_mem());
+	if (!out)
+		return;
+
+	X509_print_ex(out, cert, XN_FLAG_COMPAT, X509_FLAG_COMPAT);
+	rlen = BIO_ctrl_pending(out);
+	txt = os_malloc(rlen + 1);
+	if (txt) {
+		int res = BIO_read(out, txt, rlen);
+		if (res > 0) {
+			txt[res] = '\0';
+			wpa_printf(MSG_MSGDUMP, "%s:\n%s", title, txt);
+		}
+		os_free(txt);
+	}
+	BIO_free(out);
+}
+
+
+static void add_alt_name_othername(struct http_ctx *ctx, struct http_cert *cert,
+				   OTHERNAME *o)
+{
+	char txt[100];
+	int res;
+	struct http_othername *on;
+	ASN1_TYPE *val;
+
+	on = os_realloc_array(cert->othername, cert->num_othername + 1,
+			      sizeof(struct http_othername));
+	if (on == NULL)
+		return;
+	cert->othername = on;
+	on = &on[cert->num_othername];
+	os_memset(on, 0, sizeof(*on));
+
+	res = OBJ_obj2txt(txt, sizeof(txt), o->type_id, 1);
+	if (res < 0 || res >= (int) sizeof(txt))
+		return;
+
+	on->oid = os_strdup(txt);
+	if (on->oid == NULL)
+		return;
+
+	val = o->value;
+	on->data = val->value.octet_string->data;
+	on->len = val->value.octet_string->length;
+
+	cert->num_othername++;
+}
+
+
+static void add_alt_name_dns(struct http_ctx *ctx, struct http_cert *cert,
+			     ASN1_STRING *name)
+{
+	char *buf;
+	char **n;
+
+	buf = NULL;
+	if (ASN1_STRING_to_UTF8((unsigned char **) &buf, name) < 0)
+		return;
+
+	n = os_realloc_array(cert->dnsname, cert->num_dnsname + 1,
+			     sizeof(char *));
+	if (n == NULL)
+		return;
+
+	cert->dnsname = n;
+	n[cert->num_dnsname] = buf;
+	cert->num_dnsname++;
+}
+
+
+static void add_alt_name(struct http_ctx *ctx, struct http_cert *cert,
+			 const GENERAL_NAME *name)
+{
+	switch (name->type) {
+	case GEN_OTHERNAME:
+		add_alt_name_othername(ctx, cert, name->d.otherName);
+		break;
+	case GEN_DNS:
+		add_alt_name_dns(ctx, cert, name->d.dNSName);
+		break;
+	}
+}
+
+
+static void add_alt_names(struct http_ctx *ctx, struct http_cert *cert,
+			  GENERAL_NAMES *names)
+{
+	int num, i;
+
+	num = sk_GENERAL_NAME_num(names);
+	for (i = 0; i < num; i++) {
+		const GENERAL_NAME *name;
+		name = sk_GENERAL_NAME_value(names, i);
+		add_alt_name(ctx, cert, name);
+	}
+}
+
+
+/* RFC 3709 */
+
+typedef struct {
+	X509_ALGOR *hashAlg;
+	ASN1_OCTET_STRING *hashValue;
+} HashAlgAndValue;
+
+typedef struct {
+	STACK_OF(HashAlgAndValue) *refStructHash;
+	STACK_OF(ASN1_IA5STRING) *refStructURI;
+} LogotypeReference;
+
+typedef struct {
+	ASN1_IA5STRING *mediaType;
+	STACK_OF(HashAlgAndValue) *logotypeHash;
+	STACK_OF(ASN1_IA5STRING) *logotypeURI;
+} LogotypeDetails;
+
+typedef struct {
+	int type;
+	union {
+		ASN1_INTEGER *numBits;
+		ASN1_INTEGER *tableSize;
+	} d;
+} LogotypeImageResolution;
+
+typedef struct {
+	ASN1_INTEGER *type; /* LogotypeImageType ::= INTEGER */
+	ASN1_INTEGER *fileSize;
+	ASN1_INTEGER *xSize;
+	ASN1_INTEGER *ySize;
+	LogotypeImageResolution *resolution;
+	ASN1_IA5STRING *language;
+} LogotypeImageInfo;
+
+typedef struct {
+	LogotypeDetails *imageDetails;
+	LogotypeImageInfo *imageInfo;
+} LogotypeImage;
+
+typedef struct {
+	ASN1_INTEGER *fileSize;
+	ASN1_INTEGER *playTime;
+	ASN1_INTEGER *channels;
+	ASN1_INTEGER *sampleRate;
+	ASN1_IA5STRING *language;
+} LogotypeAudioInfo;
+
+typedef struct {
+	LogotypeDetails *audioDetails;
+	LogotypeAudioInfo *audioInfo;
+} LogotypeAudio;
+
+typedef struct {
+	STACK_OF(LogotypeImage) *image;
+	STACK_OF(LogotypeAudio) *audio;
+} LogotypeData;
+
+typedef struct {
+	int type;
+	union {
+		LogotypeData *direct;
+		LogotypeReference *indirect;
+	} d;
+} LogotypeInfo;
+
+typedef struct {
+	ASN1_OBJECT *logotypeType;
+	LogotypeInfo *info;
+} OtherLogotypeInfo;
+
+typedef struct {
+	STACK_OF(LogotypeInfo) *communityLogos;
+	LogotypeInfo *issuerLogo;
+	LogotypeInfo *subjectLogo;
+	STACK_OF(OtherLogotypeInfo) *otherLogos;
+} LogotypeExtn;
+
+ASN1_SEQUENCE(HashAlgAndValue) = {
+	ASN1_SIMPLE(HashAlgAndValue, hashAlg, X509_ALGOR),
+	ASN1_SIMPLE(HashAlgAndValue, hashValue, ASN1_OCTET_STRING)
+} ASN1_SEQUENCE_END(HashAlgAndValue);
+
+ASN1_SEQUENCE(LogotypeReference) = {
+	ASN1_SEQUENCE_OF(LogotypeReference, refStructHash, HashAlgAndValue),
+	ASN1_SEQUENCE_OF(LogotypeReference, refStructURI, ASN1_IA5STRING)
+} ASN1_SEQUENCE_END(LogotypeReference);
+
+ASN1_SEQUENCE(LogotypeDetails) = {
+	ASN1_SIMPLE(LogotypeDetails, mediaType, ASN1_IA5STRING),
+	ASN1_SEQUENCE_OF(LogotypeDetails, logotypeHash, HashAlgAndValue),
+	ASN1_SEQUENCE_OF(LogotypeDetails, logotypeURI, ASN1_IA5STRING)
+} ASN1_SEQUENCE_END(LogotypeDetails);
+
+ASN1_CHOICE(LogotypeImageResolution) = {
+	ASN1_IMP(LogotypeImageResolution, d.numBits, ASN1_INTEGER, 1),
+	ASN1_IMP(LogotypeImageResolution, d.tableSize, ASN1_INTEGER, 2)
+} ASN1_CHOICE_END(LogotypeImageResolution);
+
+ASN1_SEQUENCE(LogotypeImageInfo) = {
+	ASN1_IMP_OPT(LogotypeImageInfo, type, ASN1_INTEGER, 0),
+	ASN1_SIMPLE(LogotypeImageInfo, fileSize, ASN1_INTEGER),
+	ASN1_SIMPLE(LogotypeImageInfo, xSize, ASN1_INTEGER),
+	ASN1_SIMPLE(LogotypeImageInfo, ySize, ASN1_INTEGER),
+	ASN1_OPT(LogotypeImageInfo, resolution, LogotypeImageResolution),
+	ASN1_IMP_OPT(LogotypeImageInfo, language, ASN1_IA5STRING, 4),
+} ASN1_SEQUENCE_END(LogotypeImageInfo);
+
+ASN1_SEQUENCE(LogotypeImage) = {
+	ASN1_SIMPLE(LogotypeImage, imageDetails, LogotypeDetails),
+	ASN1_OPT(LogotypeImage, imageInfo, LogotypeImageInfo)
+} ASN1_SEQUENCE_END(LogotypeImage);
+
+ASN1_SEQUENCE(LogotypeAudioInfo) = {
+	ASN1_SIMPLE(LogotypeAudioInfo, fileSize, ASN1_INTEGER),
+	ASN1_SIMPLE(LogotypeAudioInfo, playTime, ASN1_INTEGER),
+	ASN1_SIMPLE(LogotypeAudioInfo, channels, ASN1_INTEGER),
+	ASN1_IMP_OPT(LogotypeAudioInfo, sampleRate, ASN1_INTEGER, 3),
+	ASN1_IMP_OPT(LogotypeAudioInfo, language, ASN1_IA5STRING, 4)
+} ASN1_SEQUENCE_END(LogotypeAudioInfo);
+
+ASN1_SEQUENCE(LogotypeAudio) = {
+	ASN1_SIMPLE(LogotypeAudio, audioDetails, LogotypeDetails),
+	ASN1_OPT(LogotypeAudio, audioInfo, LogotypeAudioInfo)
+} ASN1_SEQUENCE_END(LogotypeAudio);
+
+ASN1_SEQUENCE(LogotypeData) = {
+	ASN1_SEQUENCE_OF_OPT(LogotypeData, image, LogotypeImage),
+	ASN1_IMP_SEQUENCE_OF_OPT(LogotypeData, audio, LogotypeAudio, 1)
+} ASN1_SEQUENCE_END(LogotypeData);
+
+ASN1_CHOICE(LogotypeInfo) = {
+	ASN1_IMP(LogotypeInfo, d.direct, LogotypeData, 0),
+	ASN1_IMP(LogotypeInfo, d.indirect, LogotypeReference, 1)
+} ASN1_CHOICE_END(LogotypeInfo);
+
+ASN1_SEQUENCE(OtherLogotypeInfo) = {
+	ASN1_SIMPLE(OtherLogotypeInfo, logotypeType, ASN1_OBJECT),
+	ASN1_SIMPLE(OtherLogotypeInfo, info, LogotypeInfo)
+} ASN1_SEQUENCE_END(OtherLogotypeInfo);
+
+ASN1_SEQUENCE(LogotypeExtn) = {
+	ASN1_EXP_SEQUENCE_OF_OPT(LogotypeExtn, communityLogos, LogotypeInfo, 0),
+	ASN1_EXP_OPT(LogotypeExtn, issuerLogo, LogotypeInfo, 1),
+	ASN1_EXP_OPT(LogotypeExtn, issuerLogo, LogotypeInfo, 2),
+	ASN1_EXP_SEQUENCE_OF_OPT(LogotypeExtn, otherLogos, OtherLogotypeInfo, 3)
+} ASN1_SEQUENCE_END(LogotypeExtn);
+
+IMPLEMENT_ASN1_FUNCTIONS(LogotypeExtn);
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+#define sk_LogotypeInfo_num(st) SKM_sk_num(LogotypeInfo, (st))
+#define sk_LogotypeInfo_value(st, i) SKM_sk_value(LogotypeInfo, (st), (i))
+#define sk_LogotypeImage_num(st) SKM_sk_num(LogotypeImage, (st))
+#define sk_LogotypeImage_value(st, i) SKM_sk_value(LogotypeImage, (st), (i))
+#define sk_LogotypeAudio_num(st) SKM_sk_num(LogotypeAudio, (st))
+#define sk_LogotypeAudio_value(st, i) SKM_sk_value(LogotypeAudio, (st), (i))
+#define sk_HashAlgAndValue_num(st) SKM_sk_num(HashAlgAndValue, (st))
+#define sk_HashAlgAndValue_value(st, i) SKM_sk_value(HashAlgAndValue, (st), (i))
+#define sk_ASN1_IA5STRING_num(st) SKM_sk_num(ASN1_IA5STRING, (st))
+#define sk_ASN1_IA5STRING_value(st, i) SKM_sk_value(ASN1_IA5STRING, (st), (i))
+#else
+DEFINE_STACK_OF(LogotypeInfo)
+DEFINE_STACK_OF(LogotypeImage)
+DEFINE_STACK_OF(LogotypeAudio)
+DEFINE_STACK_OF(HashAlgAndValue)
+DEFINE_STACK_OF(ASN1_IA5STRING)
+#endif
+
+
+static void add_logo(struct http_ctx *ctx, struct http_cert *hcert,
+		     HashAlgAndValue *hash, ASN1_IA5STRING *uri)
+{
+	char txt[100];
+	int res, len;
+	struct http_logo *n;
+
+	if (hash == NULL || uri == NULL)
+		return;
+
+	res = OBJ_obj2txt(txt, sizeof(txt), hash->hashAlg->algorithm, 1);
+	if (res < 0 || res >= (int) sizeof(txt))
+		return;
+
+	n = os_realloc_array(hcert->logo, hcert->num_logo + 1,
+			     sizeof(struct http_logo));
+	if (n == NULL)
+		return;
+	hcert->logo = n;
+	n = &hcert->logo[hcert->num_logo];
+	os_memset(n, 0, sizeof(*n));
+
+	n->alg_oid = os_strdup(txt);
+	if (n->alg_oid == NULL)
+		return;
+
+	n->hash_len = ASN1_STRING_length(hash->hashValue);
+	n->hash = os_memdup(ASN1_STRING_get0_data(hash->hashValue),
+			    n->hash_len);
+	if (n->hash == NULL) {
+		os_free(n->alg_oid);
+		return;
+	}
+
+	len = ASN1_STRING_length(uri);
+	n->uri = os_malloc(len + 1);
+	if (n->uri == NULL) {
+		os_free(n->alg_oid);
+		os_free(n->hash);
+		return;
+	}
+	os_memcpy(n->uri, ASN1_STRING_get0_data(uri), len);
+	n->uri[len] = '\0';
+
+	hcert->num_logo++;
+}
+
+
+static void add_logo_direct(struct http_ctx *ctx, struct http_cert *hcert,
+			    LogotypeData *data)
+{
+	int i, num;
+
+	if (data->image == NULL)
+		return;
+
+	num = sk_LogotypeImage_num(data->image);
+	for (i = 0; i < num; i++) {
+		LogotypeImage *image;
+		LogotypeDetails *details;
+		int j, hash_num, uri_num;
+		HashAlgAndValue *found_hash = NULL;
+
+		image = sk_LogotypeImage_value(data->image, i);
+		if (image == NULL)
+			continue;
+
+		details = image->imageDetails;
+		if (details == NULL)
+			continue;
+
+		hash_num = sk_HashAlgAndValue_num(details->logotypeHash);
+		for (j = 0; j < hash_num; j++) {
+			HashAlgAndValue *hash;
+			char txt[100];
+			int res;
+			hash = sk_HashAlgAndValue_value(details->logotypeHash,
+							j);
+			if (hash == NULL)
+				continue;
+			res = OBJ_obj2txt(txt, sizeof(txt),
+					  hash->hashAlg->algorithm, 1);
+			if (res < 0 || res >= (int) sizeof(txt))
+				continue;
+			if (os_strcmp(txt, "2.16.840.1.101.3.4.2.1") == 0) {
+				found_hash = hash;
+				break;
+			}
+		}
+
+		if (!found_hash) {
+			wpa_printf(MSG_DEBUG, "OpenSSL: No SHA256 hash found for the logo");
+			continue;
+		}
+
+		uri_num = sk_ASN1_IA5STRING_num(details->logotypeURI);
+		for (j = 0; j < uri_num; j++) {
+			ASN1_IA5STRING *uri;
+			uri = sk_ASN1_IA5STRING_value(details->logotypeURI, j);
+			add_logo(ctx, hcert, found_hash, uri);
+		}
+	}
+}
+
+
+static void add_logo_indirect(struct http_ctx *ctx, struct http_cert *hcert,
+			      LogotypeReference *ref)
+{
+	int j, hash_num, uri_num;
+
+	hash_num = sk_HashAlgAndValue_num(ref->refStructHash);
+	uri_num = sk_ASN1_IA5STRING_num(ref->refStructURI);
+	if (hash_num != uri_num) {
+		wpa_printf(MSG_INFO, "Unexpected LogotypeReference array size difference %d != %d",
+			   hash_num, uri_num);
+		return;
+	}
+
+	for (j = 0; j < hash_num; j++) {
+		HashAlgAndValue *hash;
+		ASN1_IA5STRING *uri;
+		hash = sk_HashAlgAndValue_value(ref->refStructHash, j);
+		uri = sk_ASN1_IA5STRING_value(ref->refStructURI, j);
+		add_logo(ctx, hcert, hash, uri);
+	}
+}
+
+
+static void i2r_HashAlgAndValue(HashAlgAndValue *hash, BIO *out, int indent)
+{
+	int i;
+	const unsigned char *data;
+
+	BIO_printf(out, "%*shashAlg: ", indent, "");
+	i2a_ASN1_OBJECT(out, hash->hashAlg->algorithm);
+	BIO_printf(out, "\n");
+
+	BIO_printf(out, "%*shashValue: ", indent, "");
+	data = hash->hashValue->data;
+	for (i = 0; i < hash->hashValue->length; i++)
+		BIO_printf(out, "%s%02x", i > 0 ? ":" : "", data[i]);
+	BIO_printf(out, "\n");
+}
+
+static void i2r_LogotypeDetails(LogotypeDetails *details, BIO *out, int indent)
+{
+	int i, num;
+
+	BIO_printf(out, "%*sLogotypeDetails\n", indent, "");
+	if (details->mediaType) {
+		BIO_printf(out, "%*smediaType: ", indent, "");
+		ASN1_STRING_print(out, details->mediaType);
+		BIO_printf(out, "\n");
+	}
+
+	num = details->logotypeHash ?
+		sk_HashAlgAndValue_num(details->logotypeHash) : 0;
+	for (i = 0; i < num; i++) {
+		HashAlgAndValue *hash;
+		hash = sk_HashAlgAndValue_value(details->logotypeHash, i);
+		i2r_HashAlgAndValue(hash, out, indent);
+	}
+
+	num = details->logotypeURI ?
+		sk_ASN1_IA5STRING_num(details->logotypeURI) : 0;
+	for (i = 0; i < num; i++) {
+		ASN1_IA5STRING *uri;
+		uri = sk_ASN1_IA5STRING_value(details->logotypeURI, i);
+		BIO_printf(out, "%*slogotypeURI: ", indent, "");
+		ASN1_STRING_print(out, uri);
+		BIO_printf(out, "\n");
+	}
+}
+
+static void i2r_LogotypeImageInfo(LogotypeImageInfo *info, BIO *out, int indent)
+{
+	long val;
+
+	BIO_printf(out, "%*sLogotypeImageInfo\n", indent, "");
+	if (info->type) {
+		val = ASN1_INTEGER_get(info->type);
+		BIO_printf(out, "%*stype: %ld\n", indent, "", val);
+	} else {
+		BIO_printf(out, "%*stype: default (1)\n", indent, "");
+	}
+	val = ASN1_INTEGER_get(info->fileSize);
+	BIO_printf(out, "%*sfileSize: %ld\n", indent, "", val);
+	val = ASN1_INTEGER_get(info->xSize);
+	BIO_printf(out, "%*sxSize: %ld\n", indent, "", val);
+	val = ASN1_INTEGER_get(info->ySize);
+	BIO_printf(out, "%*sySize: %ld\n", indent, "", val);
+	if (info->resolution) {
+		BIO_printf(out, "%*sresolution [%d]\n", indent, "",
+			   info->resolution->type);
+		switch (info->resolution->type) {
+		case 0:
+			val = ASN1_INTEGER_get(info->resolution->d.numBits);
+			BIO_printf(out, "%*snumBits: %ld\n", indent, "", val);
+			break;
+		case 1:
+			val = ASN1_INTEGER_get(info->resolution->d.tableSize);
+			BIO_printf(out, "%*stableSize: %ld\n", indent, "", val);
+			break;
+		}
+	}
+	if (info->language) {
+		BIO_printf(out, "%*slanguage: ", indent, "");
+		ASN1_STRING_print(out, info->language);
+		BIO_printf(out, "\n");
+	}
+}
+
+static void i2r_LogotypeImage(LogotypeImage *image, BIO *out, int indent)
+{
+	BIO_printf(out, "%*sLogotypeImage\n", indent, "");
+	if (image->imageDetails) {
+		i2r_LogotypeDetails(image->imageDetails, out, indent + 4);
+	}
+	if (image->imageInfo) {
+		i2r_LogotypeImageInfo(image->imageInfo, out, indent + 4);
+	}
+}
+
+static void i2r_LogotypeData(LogotypeData *data, const char *title, BIO *out,
+			     int indent)
+{
+	int i, num;
+
+	BIO_printf(out, "%*s%s - LogotypeData\n", indent, "", title);
+
+	num = data->image ? sk_LogotypeImage_num(data->image) : 0;
+	for (i = 0; i < num; i++) {
+		LogotypeImage *image = sk_LogotypeImage_value(data->image, i);
+		i2r_LogotypeImage(image, out, indent + 4);
+	}
+
+	num = data->audio ? sk_LogotypeAudio_num(data->audio) : 0;
+	for (i = 0; i < num; i++) {
+		BIO_printf(out, "%*saudio: TODO\n", indent, "");
+	}
+}
+
+static void i2r_LogotypeReference(LogotypeReference *ref, const char *title,
+				  BIO *out, int indent)
+{
+	int i, hash_num, uri_num;
+
+	BIO_printf(out, "%*s%s - LogotypeReference\n", indent, "", title);
+
+	hash_num = ref->refStructHash ?
+		sk_HashAlgAndValue_num(ref->refStructHash) : 0;
+	uri_num = ref->refStructURI ?
+		sk_ASN1_IA5STRING_num(ref->refStructURI) : 0;
+	if (hash_num != uri_num) {
+		BIO_printf(out, "%*sUnexpected LogotypeReference array size difference %d != %d\n",
+			   indent, "", hash_num, uri_num);
+		return;
+	}
+
+	for (i = 0; i < hash_num; i++) {
+		HashAlgAndValue *hash;
+		ASN1_IA5STRING *uri;
+
+		hash = sk_HashAlgAndValue_value(ref->refStructHash, i);
+		i2r_HashAlgAndValue(hash, out, indent);
+
+		uri = sk_ASN1_IA5STRING_value(ref->refStructURI, i);
+		BIO_printf(out, "%*srefStructURI: ", indent, "");
+		ASN1_STRING_print(out, uri);
+		BIO_printf(out, "\n");
+	}
+}
+
+static void i2r_LogotypeInfo(LogotypeInfo *info, const char *title, BIO *out,
+			     int indent)
+{
+	switch (info->type) {
+	case 0:
+		i2r_LogotypeData(info->d.direct, title, out, indent);
+		break;
+	case 1:
+		i2r_LogotypeReference(info->d.indirect, title, out, indent);
+		break;
+	}
+}
+
+static void debug_print_logotypeext(LogotypeExtn *logo)
+{
+	BIO *out;
+	int i, num;
+	int indent = 0;
+
+	out = BIO_new_fp(stdout, BIO_NOCLOSE);
+	if (out == NULL)
+		return;
+
+	if (logo->communityLogos) {
+		num = sk_LogotypeInfo_num(logo->communityLogos);
+		for (i = 0; i < num; i++) {
+			LogotypeInfo *info;
+			info = sk_LogotypeInfo_value(logo->communityLogos, i);
+			i2r_LogotypeInfo(info, "communityLogo", out, indent);
+		}
+	}
+
+	if (logo->issuerLogo) {
+		i2r_LogotypeInfo(logo->issuerLogo, "issuerLogo", out, indent );
+	}
+
+	if (logo->subjectLogo) {
+		i2r_LogotypeInfo(logo->subjectLogo, "subjectLogo", out, indent);
+	}
+
+	if (logo->otherLogos) {
+		BIO_printf(out, "%*sotherLogos - TODO\n", indent, "");
+	}
+
+	BIO_free(out);
+}
+
+
+static void add_logotype_ext(struct http_ctx *ctx, struct http_cert *hcert,
+			     X509 *cert)
+{
+	ASN1_OBJECT *obj;
+	int pos;
+	X509_EXTENSION *ext;
+	ASN1_OCTET_STRING *os;
+	LogotypeExtn *logo;
+	const unsigned char *data;
+	int i, num;
+
+	obj = OBJ_txt2obj("1.3.6.1.5.5.7.1.12", 0);
+	if (obj == NULL)
+		return;
+
+	pos = X509_get_ext_by_OBJ(cert, obj, -1);
+	if (pos < 0) {
+		wpa_printf(MSG_INFO, "No logotype extension included");
+		return;
+	}
+
+	wpa_printf(MSG_INFO, "Parsing logotype extension");
+	ext = X509_get_ext(cert, pos);
+	if (!ext) {
+		wpa_printf(MSG_INFO, "Could not get logotype extension");
+		return;
+	}
+
+	os = X509_EXTENSION_get_data(ext);
+	if (os == NULL) {
+		wpa_printf(MSG_INFO, "Could not get logotype extension data");
+		return;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "logotypeExtn",
+		    ASN1_STRING_get0_data(os), ASN1_STRING_length(os));
+
+	data = ASN1_STRING_get0_data(os);
+	logo = d2i_LogotypeExtn(NULL, &data, ASN1_STRING_length(os));
+	if (logo == NULL) {
+		wpa_printf(MSG_INFO, "Failed to parse logotypeExtn");
+		return;
+	}
+
+	if (wpa_debug_level < MSG_INFO)
+		debug_print_logotypeext(logo);
+
+	if (!logo->communityLogos) {
+		wpa_printf(MSG_INFO, "No communityLogos included");
+		LogotypeExtn_free(logo);
+		return;
+	}
+
+	num = sk_LogotypeInfo_num(logo->communityLogos);
+	for (i = 0; i < num; i++) {
+		LogotypeInfo *info;
+		info = sk_LogotypeInfo_value(logo->communityLogos, i);
+		switch (info->type) {
+		case 0:
+			add_logo_direct(ctx, hcert, info->d.direct);
+			break;
+		case 1:
+			add_logo_indirect(ctx, hcert, info->d.indirect);
+			break;
+		}
+	}
+
+	LogotypeExtn_free(logo);
+}
+
+
+static void parse_cert(struct http_ctx *ctx, struct http_cert *hcert,
+		       X509 *cert, GENERAL_NAMES **names)
+{
+	os_memset(hcert, 0, sizeof(*hcert));
+	hcert->url = ctx->url ? ctx->url : ctx->svc_address;
+
+	*names = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
+	if (*names)
+		add_alt_names(ctx, hcert, *names);
+
+	add_logotype_ext(ctx, hcert, cert);
+}
+
+
+static void parse_cert_free(struct http_cert *hcert, GENERAL_NAMES *names)
+{
+	unsigned int i;
+
+	for (i = 0; i < hcert->num_dnsname; i++)
+		OPENSSL_free(hcert->dnsname[i]);
+	os_free(hcert->dnsname);
+
+	for (i = 0; i < hcert->num_othername; i++)
+		os_free(hcert->othername[i].oid);
+	os_free(hcert->othername);
+
+	for (i = 0; i < hcert->num_logo; i++) {
+		os_free(hcert->logo[i].alg_oid);
+		os_free(hcert->logo[i].hash);
+		os_free(hcert->logo[i].uri);
+	}
+	os_free(hcert->logo);
+
+	sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free);
+}
+
+
+static int validate_server_cert(struct http_ctx *ctx, X509 *cert)
+{
+	GENERAL_NAMES *names;
+	struct http_cert hcert;
+	int ret;
+
+	if (ctx->cert_cb == NULL) {
+		wpa_printf(MSG_DEBUG, "%s: no cert_cb configured", __func__);
+		return 0;
+	}
+
+	if (0) {
+		BIO *out;
+		out = BIO_new_fp(stdout, BIO_NOCLOSE);
+		X509_print_ex(out, cert, XN_FLAG_COMPAT, X509_FLAG_COMPAT);
+		BIO_free(out);
+	}
+
+	parse_cert(ctx, &hcert, cert, &names);
+	ret = ctx->cert_cb(ctx->cert_cb_ctx, &hcert);
+	parse_cert_free(&hcert, names);
+
+	return ret;
+}
+
+
+void http_parse_x509_certificate(struct http_ctx *ctx, const char *fname)
+{
+	BIO *in, *out;
+	X509 *cert;
+	GENERAL_NAMES *names;
+	struct http_cert hcert;
+	unsigned int i;
+
+	in = BIO_new_file(fname, "r");
+	if (in == NULL) {
+		wpa_printf(MSG_ERROR, "Could not read '%s'", fname);
+		return;
+	}
+
+	cert = d2i_X509_bio(in, NULL);
+	BIO_free(in);
+
+	if (cert == NULL) {
+		wpa_printf(MSG_ERROR, "Could not parse certificate");
+		return;
+	}
+
+	out = BIO_new_fp(stdout, BIO_NOCLOSE);
+	if (out) {
+		X509_print_ex(out, cert, XN_FLAG_COMPAT,
+			      X509_FLAG_COMPAT);
+		BIO_free(out);
+	}
+
+	wpa_printf(MSG_INFO, "Additional parsing information:");
+	parse_cert(ctx, &hcert, cert, &names);
+	for (i = 0; i < hcert.num_othername; i++) {
+		if (os_strcmp(hcert.othername[i].oid,
+			      "1.3.6.1.4.1.40808.1.1.1") == 0) {
+			char *name = os_zalloc(hcert.othername[i].len + 1);
+			if (name) {
+				os_memcpy(name, hcert.othername[i].data,
+					  hcert.othername[i].len);
+				wpa_printf(MSG_INFO,
+					   "id-wfa-hotspot-friendlyName: %s",
+					   name);
+				os_free(name);
+			}
+			wpa_hexdump_ascii(MSG_INFO,
+					  "id-wfa-hotspot-friendlyName",
+					  hcert.othername[i].data,
+					  hcert.othername[i].len);
+		} else {
+			wpa_printf(MSG_INFO, "subjAltName[othername]: oid=%s",
+				   hcert.othername[i].oid);
+			wpa_hexdump_ascii(MSG_INFO, "unknown othername",
+					  hcert.othername[i].data,
+					  hcert.othername[i].len);
+		}
+	}
+	parse_cert_free(&hcert, names);
+
+	X509_free(cert);
+}
+
+
+static int curl_cb_ssl_verify(int preverify_ok, X509_STORE_CTX *x509_ctx)
+{
+	struct http_ctx *ctx;
+	X509 *cert;
+	int err, depth;
+	char buf[256];
+	X509_NAME *name;
+	const char *err_str;
+	SSL *ssl;
+	SSL_CTX *ssl_ctx;
+
+	ssl = X509_STORE_CTX_get_ex_data(x509_ctx,
+					 SSL_get_ex_data_X509_STORE_CTX_idx());
+	ssl_ctx = SSL_get_SSL_CTX(ssl);
+	ctx = SSL_CTX_get_app_data(ssl_ctx);
+
+	wpa_printf(MSG_DEBUG, "curl_cb_ssl_verify, preverify_ok: %d",
+		   preverify_ok);
+
+	err = X509_STORE_CTX_get_error(x509_ctx);
+	err_str = X509_verify_cert_error_string(err);
+	depth = X509_STORE_CTX_get_error_depth(x509_ctx);
+	cert = X509_STORE_CTX_get_current_cert(x509_ctx);
+	if (!cert) {
+		wpa_printf(MSG_INFO, "No server certificate available");
+		ctx->last_err = "No server certificate available";
+		return 0;
+	}
+
+	if (depth == 0)
+		ctx->peer_cert = cert;
+	else if (depth == 1)
+		ctx->peer_issuer = cert;
+	else if (depth == 2)
+		ctx->peer_issuer_issuer = cert;
+
+	name = X509_get_subject_name(cert);
+	X509_NAME_oneline(name, buf, sizeof(buf));
+	wpa_printf(MSG_INFO, "Server certificate chain - depth=%d err=%d (%s) subject=%s",
+		   depth, err, err_str, buf);
+	debug_dump_cert("Server certificate chain - certificate", cert);
+
+	if (depth == 0 && preverify_ok && validate_server_cert(ctx, cert) < 0)
+		return 0;
+
+#ifdef OPENSSL_IS_BORINGSSL
+	if (depth == 0 && ctx->ocsp != NO_OCSP && preverify_ok) {
+		enum ocsp_result res;
+
+		res = check_ocsp_resp(ssl_ctx, ssl, cert, ctx->peer_issuer,
+				      ctx->peer_issuer_issuer);
+		if (res == OCSP_REVOKED) {
+			preverify_ok = 0;
+			wpa_printf(MSG_INFO, "OCSP: certificate revoked");
+			if (err == X509_V_OK)
+				X509_STORE_CTX_set_error(
+					x509_ctx, X509_V_ERR_CERT_REVOKED);
+		} else if (res != OCSP_GOOD && (ctx->ocsp == MANDATORY_OCSP)) {
+			preverify_ok = 0;
+			wpa_printf(MSG_INFO,
+				   "OCSP: bad certificate status response");
+		}
+	}
+#endif /* OPENSSL_IS_BORINGSSL */
+
+	if (!preverify_ok)
+		ctx->last_err = "TLS validation failed";
+
+	return preverify_ok;
+}
+
+
+#ifdef HAVE_OCSP
+
+static void ocsp_debug_print_resp(OCSP_RESPONSE *rsp)
+{
+	BIO *out;
+	size_t rlen;
+	char *txt;
+	int res;
+
+	out = BIO_new(BIO_s_mem());
+	if (!out)
+		return;
+
+	OCSP_RESPONSE_print(out, rsp, 0);
+	rlen = BIO_ctrl_pending(out);
+	txt = os_malloc(rlen + 1);
+	if (!txt) {
+		BIO_free(out);
+		return;
+	}
+
+	res = BIO_read(out, txt, rlen);
+	if (res > 0) {
+		txt[res] = '\0';
+		wpa_printf(MSG_MSGDUMP, "OpenSSL: OCSP Response\n%s", txt);
+	}
+	os_free(txt);
+	BIO_free(out);
+}
+
+
+static void tls_show_errors(const char *func, const char *txt)
+{
+	unsigned long err;
+
+	wpa_printf(MSG_DEBUG, "OpenSSL: %s - %s %s",
+		   func, txt, ERR_error_string(ERR_get_error(), NULL));
+
+	while ((err = ERR_get_error())) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: pending error: %s",
+			   ERR_error_string(err, NULL));
+	}
+}
+
+
+static int ocsp_resp_cb(SSL *s, void *arg)
+{
+	struct http_ctx *ctx = arg;
+	const unsigned char *p;
+	int len, status, reason, res;
+	OCSP_RESPONSE *rsp;
+	OCSP_BASICRESP *basic;
+	OCSP_CERTID *id;
+	ASN1_GENERALIZEDTIME *produced_at, *this_update, *next_update;
+	X509_STORE *store;
+	STACK_OF(X509) *certs = NULL;
+
+	len = SSL_get_tlsext_status_ocsp_resp(s, &p);
+	if (!p) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: No OCSP response received");
+		if (ctx->ocsp == MANDATORY_OCSP)
+			ctx->last_err = "No OCSP response received";
+		return (ctx->ocsp == MANDATORY_OCSP) ? 0 : 1;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "OpenSSL: OCSP response", p, len);
+
+	rsp = d2i_OCSP_RESPONSE(NULL, &p, len);
+	if (!rsp) {
+		wpa_printf(MSG_INFO, "OpenSSL: Failed to parse OCSP response");
+		ctx->last_err = "Failed to parse OCSP response";
+		return 0;
+	}
+
+	ocsp_debug_print_resp(rsp);
+
+	status = OCSP_response_status(rsp);
+	if (status != OCSP_RESPONSE_STATUS_SUCCESSFUL) {
+		wpa_printf(MSG_INFO, "OpenSSL: OCSP responder error %d (%s)",
+			   status, OCSP_response_status_str(status));
+		ctx->last_err = "OCSP responder error";
+		return 0;
+	}
+
+	basic = OCSP_response_get1_basic(rsp);
+	if (!basic) {
+		wpa_printf(MSG_INFO, "OpenSSL: Could not find BasicOCSPResponse");
+		ctx->last_err = "Could not find BasicOCSPResponse";
+		return 0;
+	}
+
+	store = SSL_CTX_get_cert_store(SSL_get_SSL_CTX(s));
+	if (ctx->peer_issuer) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: Add issuer");
+		debug_dump_cert("OpenSSL: Issuer certificate",
+				ctx->peer_issuer);
+
+		if (X509_STORE_add_cert(store, ctx->peer_issuer) != 1) {
+			tls_show_errors(__func__,
+					"OpenSSL: Could not add issuer to certificate store");
+		}
+		certs = sk_X509_new_null();
+		if (certs) {
+			X509 *cert;
+			cert = X509_dup(ctx->peer_issuer);
+			if (cert && !sk_X509_push(certs, cert)) {
+				tls_show_errors(
+					__func__,
+					"OpenSSL: Could not add issuer to OCSP responder trust store");
+				X509_free(cert);
+				sk_X509_free(certs);
+				certs = NULL;
+			}
+			if (certs && ctx->peer_issuer_issuer) {
+				cert = X509_dup(ctx->peer_issuer_issuer);
+				if (cert && !sk_X509_push(certs, cert)) {
+					tls_show_errors(
+						__func__,
+						"OpenSSL: Could not add issuer's issuer to OCSP responder trust store");
+					X509_free(cert);
+				}
+			}
+		}
+	}
+
+	status = OCSP_basic_verify(basic, certs, store, OCSP_TRUSTOTHER);
+	sk_X509_pop_free(certs, X509_free);
+	if (status <= 0) {
+		tls_show_errors(__func__,
+				"OpenSSL: OCSP response failed verification");
+		OCSP_BASICRESP_free(basic);
+		OCSP_RESPONSE_free(rsp);
+		ctx->last_err = "OCSP response failed verification";
+		return 0;
+	}
+
+	wpa_printf(MSG_DEBUG, "OpenSSL: OCSP response verification succeeded");
+
+	if (!ctx->peer_cert) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: Peer certificate not available for OCSP status check");
+		OCSP_BASICRESP_free(basic);
+		OCSP_RESPONSE_free(rsp);
+		ctx->last_err = "Peer certificate not available for OCSP status check";
+		return 0;
+	}
+
+	if (!ctx->peer_issuer) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: Peer issuer certificate not available for OCSP status check");
+		OCSP_BASICRESP_free(basic);
+		OCSP_RESPONSE_free(rsp);
+		ctx->last_err = "Peer issuer certificate not available for OCSP status check";
+		return 0;
+	}
+
+	id = OCSP_cert_to_id(EVP_sha256(), ctx->peer_cert, ctx->peer_issuer);
+	if (!id) {
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: Could not create OCSP certificate identifier (SHA256)");
+		OCSP_BASICRESP_free(basic);
+		OCSP_RESPONSE_free(rsp);
+		ctx->last_err = "Could not create OCSP certificate identifier";
+		return 0;
+	}
+
+	res = OCSP_resp_find_status(basic, id, &status, &reason, &produced_at,
+				    &this_update, &next_update);
+	if (!res) {
+		id = OCSP_cert_to_id(NULL, ctx->peer_cert, ctx->peer_issuer);
+		if (!id) {
+			wpa_printf(MSG_DEBUG,
+				   "OpenSSL: Could not create OCSP certificate identifier (SHA1)");
+			OCSP_BASICRESP_free(basic);
+			OCSP_RESPONSE_free(rsp);
+			ctx->last_err =
+				"Could not create OCSP certificate identifier";
+			return 0;
+		}
+
+		res = OCSP_resp_find_status(basic, id, &status, &reason,
+					    &produced_at, &this_update,
+					    &next_update);
+	}
+
+	if (!res) {
+		wpa_printf(MSG_INFO, "OpenSSL: Could not find current server certificate from OCSP response%s",
+			   (ctx->ocsp == MANDATORY_OCSP) ? "" :
+			   " (OCSP not required)");
+		OCSP_CERTID_free(id);
+		OCSP_BASICRESP_free(basic);
+		OCSP_RESPONSE_free(rsp);
+		if (ctx->ocsp == MANDATORY_OCSP)
+
+			ctx->last_err = "Could not find current server certificate from OCSP response";
+		return (ctx->ocsp == MANDATORY_OCSP) ? 0 : 1;
+	}
+	OCSP_CERTID_free(id);
+
+	if (!OCSP_check_validity(this_update, next_update, 5 * 60, -1)) {
+		tls_show_errors(__func__, "OpenSSL: OCSP status times invalid");
+		OCSP_BASICRESP_free(basic);
+		OCSP_RESPONSE_free(rsp);
+		ctx->last_err = "OCSP status times invalid";
+		return 0;
+	}
+
+	OCSP_BASICRESP_free(basic);
+	OCSP_RESPONSE_free(rsp);
+
+	wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status for server certificate: %s",
+		   OCSP_cert_status_str(status));
+
+	if (status == V_OCSP_CERTSTATUS_GOOD)
+		return 1;
+	if (status == V_OCSP_CERTSTATUS_REVOKED) {
+		ctx->last_err = "Server certificate has been revoked";
+		return 0;
+	}
+	if (ctx->ocsp == MANDATORY_OCSP) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status unknown, but OCSP required");
+		ctx->last_err = "OCSP status unknown";
+		return 0;
+	}
+	wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status unknown, but OCSP was not required, so allow connection to continue");
+	return 1;
+}
+
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+static SSL_METHOD patch_ssl_method;
+static const SSL_METHOD *real_ssl_method;
+
+static int curl_patch_ssl_new(SSL *s)
+{
+	SSL_CTX *ssl = SSL_get_SSL_CTX(s);
+	int ret;
+
+	ssl->method = real_ssl_method;
+	s->method = real_ssl_method;
+
+	ret = s->method->ssl_new(s);
+	SSL_set_tlsext_status_type(s, TLSEXT_STATUSTYPE_ocsp);
+
+	return ret;
+}
+#endif /* OpenSSL < 1.1.0 */
+
+#endif /* HAVE_OCSP */
+
+
+static CURLcode curl_cb_ssl(CURL *curl, void *sslctx, void *parm)
+{
+	struct http_ctx *ctx = parm;
+	SSL_CTX *ssl = sslctx;
+
+	wpa_printf(MSG_DEBUG, "curl_cb_ssl");
+	SSL_CTX_set_app_data(ssl, ctx);
+	SSL_CTX_set_verify(ssl, SSL_VERIFY_PEER, curl_cb_ssl_verify);
+
+#ifdef HAVE_OCSP
+	if (ctx->ocsp != NO_OCSP) {
+		SSL_CTX_set_tlsext_status_cb(ssl, ocsp_resp_cb);
+		SSL_CTX_set_tlsext_status_arg(ssl, ctx);
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+		/*
+		 * Use a temporary SSL_METHOD to get a callback on SSL_new()
+		 * from libcurl since there is no proper callback registration
+		 * available for this.
+		 */
+		os_memset(&patch_ssl_method, 0, sizeof(patch_ssl_method));
+		patch_ssl_method.ssl_new = curl_patch_ssl_new;
+		real_ssl_method = ssl->method;
+		ssl->method = &patch_ssl_method;
+#endif /* OpenSSL < 1.1.0 */
+	}
+#endif /* HAVE_OCSP */
+
+	return CURLE_OK;
+}
+
+#endif /* EAP_TLS_OPENSSL */
+
+
+static CURL * setup_curl_post(struct http_ctx *ctx, const char *address,
+			      const char *ca_fname, const char *username,
+			      const char *password, const char *client_cert,
+			      const char *client_key)
+{
+	CURL *curl;
+#ifdef EAP_TLS_OPENSSL
+	const char *extra = " tls=openssl";
+#else /* EAP_TLS_OPENSSL */
+	const char *extra = "";
+#endif /* EAP_TLS_OPENSSL */
+
+	wpa_printf(MSG_DEBUG, "Start HTTP client: address=%s ca_fname=%s "
+		   "username=%s%s", address, ca_fname, username, extra);
+
+	curl = curl_easy_init();
+	if (curl == NULL)
+		return NULL;
+
+	curl_easy_setopt(curl, CURLOPT_URL, address);
+	curl_easy_setopt(curl, CURLOPT_POST, 1L);
+	if (ca_fname) {
+		curl_easy_setopt(curl, CURLOPT_CAINFO, ca_fname);
+		curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);
+#ifdef EAP_TLS_OPENSSL
+		curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, curl_cb_ssl);
+		curl_easy_setopt(curl, CURLOPT_SSL_CTX_DATA, ctx);
+#if defined(OPENSSL_IS_BORINGSSL) || (OPENSSL_VERSION_NUMBER >= 0x10100000L)
+		/* For now, using the CURLOPT_SSL_VERIFYSTATUS option only
+		 * with BoringSSL since the OpenSSL specific callback hack to
+		 * enable OCSP is not available with BoringSSL. The OCSP
+		 * implementation within libcurl is not sufficient for the
+		 * Hotspot 2.0 OSU needs, so cannot use this with OpenSSL.
+		 */
+		if (ctx->ocsp != NO_OCSP)
+			curl_easy_setopt(curl, CURLOPT_SSL_VERIFYSTATUS, 1L);
+#endif /* OPENSSL_IS_BORINGSSL */
+#endif /* EAP_TLS_OPENSSL */
+	} else {
+		curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
+	}
+	if (client_cert && client_key) {
+		curl_easy_setopt(curl, CURLOPT_SSLCERT, client_cert);
+		curl_easy_setopt(curl, CURLOPT_SSLKEY, client_key);
+	}
+	/* TODO: use curl_easy_getinfo() with CURLINFO_CERTINFO to fetch
+	 * information about the server certificate */
+	curl_easy_setopt(curl, CURLOPT_CERTINFO, 1L);
+	curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, curl_cb_debug);
+	curl_easy_setopt(curl, CURLOPT_DEBUGDATA, ctx);
+	curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_cb_write);
+	curl_easy_setopt(curl, CURLOPT_WRITEDATA, ctx);
+	curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
+	if (username) {
+		curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_ANYSAFE);
+		curl_easy_setopt(curl, CURLOPT_USERNAME, username);
+		curl_easy_setopt(curl, CURLOPT_PASSWORD, password);
+	}
+
+	return curl;
+}
+
+
+static int post_init_client(struct http_ctx *ctx, const char *address,
+			    const char *ca_fname, const char *username,
+			    const char *password, const char *client_cert,
+			    const char *client_key)
+{
+	char *pos;
+	int count;
+
+	clone_str(&ctx->svc_address, address);
+	clone_str(&ctx->svc_ca_fname, ca_fname);
+	clone_str(&ctx->svc_username, username);
+	clone_str(&ctx->svc_password, password);
+	clone_str(&ctx->svc_client_cert, client_cert);
+	clone_str(&ctx->svc_client_key, client_key);
+
+	/*
+	 * Workaround for Apache "Hostname 'FOO' provided via SNI and hostname
+	 * 'foo' provided via HTTP are different.
+	 */
+	for (count = 0, pos = ctx->svc_address; count < 3 && pos && *pos;
+	     pos++) {
+		if (*pos == '/')
+			count++;
+		*pos = tolower(*pos);
+	}
+
+	ctx->curl = setup_curl_post(ctx, ctx->svc_address, ca_fname, username,
+				    password, client_cert, client_key);
+	if (ctx->curl == NULL)
+		return -1;
+
+	return 0;
+}
+
+
+int soap_init_client(struct http_ctx *ctx, const char *address,
+		     const char *ca_fname, const char *username,
+		     const char *password, const char *client_cert,
+		     const char *client_key)
+{
+	if (post_init_client(ctx, address, ca_fname, username, password,
+			     client_cert, client_key) < 0)
+		return -1;
+
+	ctx->curl_hdr = curl_slist_append(ctx->curl_hdr,
+					  "Content-Type: application/soap+xml");
+	ctx->curl_hdr = curl_slist_append(ctx->curl_hdr, "SOAPAction: ");
+	ctx->curl_hdr = curl_slist_append(ctx->curl_hdr, "Expect:");
+	curl_easy_setopt(ctx->curl, CURLOPT_HTTPHEADER, ctx->curl_hdr);
+
+	return 0;
+}
+
+
+int soap_reinit_client(struct http_ctx *ctx)
+{
+	char *address = NULL;
+	char *ca_fname = NULL;
+	char *username = NULL;
+	char *password = NULL;
+	char *client_cert = NULL;
+	char *client_key = NULL;
+	int ret;
+
+	clear_curl(ctx);
+
+	clone_str(&address, ctx->svc_address);
+	clone_str(&ca_fname, ctx->svc_ca_fname);
+	clone_str(&username, ctx->svc_username);
+	clone_str(&password, ctx->svc_password);
+	clone_str(&client_cert, ctx->svc_client_cert);
+	clone_str(&client_key, ctx->svc_client_key);
+
+	ret = soap_init_client(ctx, address, ca_fname, username, password,
+			       client_cert, client_key);
+	os_free(address);
+	os_free(ca_fname);
+	str_clear_free(username);
+	str_clear_free(password);
+	os_free(client_cert);
+	os_free(client_key);
+	return ret;
+}
+
+
+static void free_curl_buf(struct http_ctx *ctx)
+{
+	os_free(ctx->curl_buf);
+	ctx->curl_buf = NULL;
+	ctx->curl_buf_len = 0;
+}
+
+
+xml_node_t * soap_send_receive(struct http_ctx *ctx, xml_node_t *node)
+{
+	char *str;
+	xml_node_t *envelope, *ret, *resp, *n;
+	CURLcode res;
+	long http = 0;
+
+	ctx->last_err = NULL;
+
+	wpa_printf(MSG_DEBUG, "SOAP: Sending message");
+	envelope = soap_build_envelope(ctx->xml, node);
+	str = xml_node_to_str(ctx->xml, envelope);
+	xml_node_free(ctx->xml, envelope);
+	wpa_printf(MSG_MSGDUMP, "SOAP[%s]", str);
+
+	curl_easy_setopt(ctx->curl, CURLOPT_POSTFIELDS, str);
+	free_curl_buf(ctx);
+
+	res = curl_easy_perform(ctx->curl);
+	if (res != CURLE_OK) {
+		if (!ctx->last_err)
+			ctx->last_err = curl_easy_strerror(res);
+		wpa_printf(MSG_ERROR, "curl_easy_perform() failed: %s",
+			   ctx->last_err);
+		os_free(str);
+		free_curl_buf(ctx);
+		return NULL;
+	}
+	os_free(str);
+
+	curl_easy_getinfo(ctx->curl, CURLINFO_RESPONSE_CODE, &http);
+	wpa_printf(MSG_DEBUG, "SOAP: Server response code %ld", http);
+	if (http != 200) {
+		ctx->last_err = "HTTP download failed";
+		wpa_printf(MSG_INFO, "HTTP download failed - code %ld", http);
+		free_curl_buf(ctx);
+		return NULL;
+	}
+
+	if (ctx->curl_buf == NULL)
+		return NULL;
+
+	wpa_printf(MSG_MSGDUMP, "Server response:\n%s", ctx->curl_buf);
+	resp = xml_node_from_buf(ctx->xml, ctx->curl_buf);
+	free_curl_buf(ctx);
+	if (resp == NULL) {
+		wpa_printf(MSG_INFO, "Could not parse SOAP response");
+		ctx->last_err = "Could not parse SOAP response";
+		return NULL;
+	}
+
+	ret = soap_get_body(ctx->xml, resp);
+	if (ret == NULL) {
+		wpa_printf(MSG_INFO, "Could not get SOAP body");
+		ctx->last_err = "Could not get SOAP body";
+		return NULL;
+	}
+
+	wpa_printf(MSG_DEBUG, "SOAP body localname: '%s'",
+		   xml_node_get_localname(ctx->xml, ret));
+	n = xml_node_copy(ctx->xml, ret);
+	xml_node_free(ctx->xml, resp);
+
+	return n;
+}
+
+
+struct http_ctx * http_init_ctx(void *upper_ctx, struct xml_node_ctx *xml_ctx)
+{
+	struct http_ctx *ctx;
+
+	ctx = os_zalloc(sizeof(*ctx));
+	if (ctx == NULL)
+		return NULL;
+	ctx->ctx = upper_ctx;
+	ctx->xml = xml_ctx;
+	ctx->ocsp = OPTIONAL_OCSP;
+
+	curl_global_init(CURL_GLOBAL_ALL);
+
+	return ctx;
+}
+
+
+void http_ocsp_set(struct http_ctx *ctx, int val)
+{
+	if (val == 0)
+		ctx->ocsp = NO_OCSP;
+	else if (val == 1)
+		ctx->ocsp = OPTIONAL_OCSP;
+	if (val == 2)
+		ctx->ocsp = MANDATORY_OCSP;
+}
+
+
+void http_deinit_ctx(struct http_ctx *ctx)
+{
+	clear_curl(ctx);
+	os_free(ctx->curl_buf);
+	curl_global_cleanup();
+
+	os_free(ctx->svc_address);
+	os_free(ctx->svc_ca_fname);
+	str_clear_free(ctx->svc_username);
+	str_clear_free(ctx->svc_password);
+	os_free(ctx->svc_client_cert);
+	os_free(ctx->svc_client_key);
+
+	os_free(ctx);
+}
+
+
+int http_download_file(struct http_ctx *ctx, const char *url,
+		       const char *fname, const char *ca_fname)
+{
+	CURL *curl;
+	FILE *f = NULL;
+	CURLcode res;
+	long http = 0;
+	int ret = -1;
+
+	ctx->last_err = NULL;
+	ctx->url = url;
+
+	wpa_printf(MSG_DEBUG, "curl: Download file from %s to %s (ca=%s)",
+		   url, fname, ca_fname);
+	curl = curl_easy_init();
+	if (curl == NULL)
+		goto fail;
+
+	f = fopen(fname, "wb");
+	if (!f)
+		goto fail;
+
+	curl_easy_setopt(curl, CURLOPT_URL, url);
+	if (ca_fname) {
+		curl_easy_setopt(curl, CURLOPT_CAINFO, ca_fname);
+		curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);
+		curl_easy_setopt(curl, CURLOPT_CERTINFO, 1L);
+	} else {
+		curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
+	}
+	curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, curl_cb_debug);
+	curl_easy_setopt(curl, CURLOPT_DEBUGDATA, ctx);
+	curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, fwrite);
+	curl_easy_setopt(curl, CURLOPT_WRITEDATA, f);
+	curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
+
+	res = curl_easy_perform(curl);
+	if (res != CURLE_OK) {
+		if (!ctx->last_err)
+			ctx->last_err = curl_easy_strerror(res);
+		wpa_printf(MSG_ERROR, "curl_easy_perform() failed: %s",
+			   ctx->last_err);
+		goto fail;
+	}
+
+	curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http);
+	wpa_printf(MSG_DEBUG, "curl: Server response code %ld", http);
+	if (http != 200) {
+		ctx->last_err = "HTTP download failed";
+		wpa_printf(MSG_INFO, "HTTP download failed - code %ld", http);
+		goto fail;
+	}
+
+	ret = 0;
+
+fail:
+	ctx->url = NULL;
+	if (curl)
+		curl_easy_cleanup(curl);
+	if (f)
+		fclose(f);
+
+	return ret;
+}
+
+
+char * http_post(struct http_ctx *ctx, const char *url, const char *data,
+		 const char *content_type, const char *ext_hdr,
+		 const char *ca_fname,
+		 const char *username, const char *password,
+		 const char *client_cert, const char *client_key,
+		 size_t *resp_len)
+{
+	long http = 0;
+	CURLcode res;
+	char *ret = NULL;
+	CURL *curl;
+	struct curl_slist *curl_hdr = NULL;
+
+	ctx->last_err = NULL;
+	ctx->url = url;
+	wpa_printf(MSG_DEBUG, "curl: HTTP POST to %s", url);
+	curl = setup_curl_post(ctx, url, ca_fname, username, password,
+			       client_cert, client_key);
+	if (curl == NULL)
+		goto fail;
+
+	if (content_type) {
+		char ct[200];
+		snprintf(ct, sizeof(ct), "Content-Type: %s", content_type);
+		curl_hdr = curl_slist_append(curl_hdr, ct);
+	}
+	if (ext_hdr)
+		curl_hdr = curl_slist_append(curl_hdr, ext_hdr);
+	curl_easy_setopt(curl, CURLOPT_HTTPHEADER, curl_hdr);
+
+	curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data);
+	free_curl_buf(ctx);
+
+	res = curl_easy_perform(curl);
+	if (res != CURLE_OK) {
+		if (!ctx->last_err)
+			ctx->last_err = curl_easy_strerror(res);
+		wpa_printf(MSG_ERROR, "curl_easy_perform() failed: %s",
+			   ctx->last_err);
+		goto fail;
+	}
+
+	curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http);
+	wpa_printf(MSG_DEBUG, "curl: Server response code %ld", http);
+	if (http != 200) {
+		ctx->last_err = "HTTP POST failed";
+		wpa_printf(MSG_INFO, "HTTP POST failed - code %ld", http);
+		goto fail;
+	}
+
+	if (ctx->curl_buf == NULL)
+		goto fail;
+
+	ret = ctx->curl_buf;
+	if (resp_len)
+		*resp_len = ctx->curl_buf_len;
+	ctx->curl_buf = NULL;
+	ctx->curl_buf_len = 0;
+
+	wpa_printf(MSG_MSGDUMP, "Server response:\n%s", ret);
+
+fail:
+	free_curl_buf(ctx);
+	ctx->url = NULL;
+	return ret;
+}
+
+
+void http_set_cert_cb(struct http_ctx *ctx,
+		      int (*cb)(void *ctx, struct http_cert *cert),
+		      void *cb_ctx)
+{
+	ctx->cert_cb = cb;
+	ctx->cert_cb_ctx = cb_ctx;
+}
+
+
+const char * http_get_err(struct http_ctx *ctx)
+{
+	return ctx->last_err;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/includes.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/includes.h
new file mode 100644
index 0000000..741fc9c
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/includes.h
@@ -0,0 +1,46 @@
+/*
+ * wpa_supplicant/hostapd - Default include files
+ * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ *
+ * This header file is included into all C files so that commonly used header
+ * files can be selected with OS specific ifdef blocks in one place instead of
+ * having to have OS/C library specific selection in many files.
+ */
+
+#ifndef INCLUDES_H
+#define INCLUDES_H
+
+/* Include possible build time configuration before including anything else */
+#include "build_config.h"
+
+#include <stdlib.h>
+#include <stddef.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#ifndef _WIN32_WCE
+#include <signal.h>
+#include <sys/types.h>
+#include <errno.h>
+#endif /* _WIN32_WCE */
+#include <ctype.h>
+
+#ifndef _MSC_VER
+#include <unistd.h>
+#endif /* _MSC_VER */
+
+#ifndef CONFIG_NATIVE_WINDOWS
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#ifndef __vxworks
+#include <sys/uio.h>
+#include <sys/time.h>
+#endif /* __vxworks */
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+#endif /* INCLUDES_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/ip_addr.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/ip_addr.c
new file mode 100644
index 0000000..a971f72
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/ip_addr.c
@@ -0,0 +1,72 @@
+/*
+ * IP address processing
+ * Copyright (c) 2003-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "ip_addr.h"
+
+const char * hostapd_ip_txt(const struct hostapd_ip_addr *addr, char *buf,
+			    size_t buflen)
+{
+	if (buflen == 0 || addr == NULL)
+		return NULL;
+
+	if (addr->af == AF_INET) {
+		os_strlcpy(buf, inet_ntoa(addr->u.v4), buflen);
+	} else {
+		buf[0] = '\0';
+	}
+#ifdef CONFIG_IPV6
+	if (addr->af == AF_INET6) {
+		if (inet_ntop(AF_INET6, &addr->u.v6, buf, buflen) == NULL)
+			buf[0] = '\0';
+	}
+#endif /* CONFIG_IPV6 */
+
+	return buf;
+}
+
+
+int hostapd_parse_ip_addr(const char *txt, struct hostapd_ip_addr *addr)
+{
+#ifndef CONFIG_NATIVE_WINDOWS
+	if (inet_aton(txt, &addr->u.v4)) {
+		addr->af = AF_INET;
+		return 0;
+	}
+
+#ifdef CONFIG_IPV6
+	if (inet_pton(AF_INET6, txt, &addr->u.v6) > 0) {
+		addr->af = AF_INET6;
+		return 0;
+	}
+#endif /* CONFIG_IPV6 */
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+	return -1;
+}
+
+
+bool hostapd_ip_equal(const struct hostapd_ip_addr *a,
+		      const struct hostapd_ip_addr *b)
+{
+	if (a->af != b->af)
+		return false;
+
+	if (a->af == AF_INET && a->u.v4.s_addr == b->u.v4.s_addr)
+		return true;
+
+#ifdef CONFIG_IPV6
+	if (a->af == AF_INET6 &&
+	    os_memcmp(&a->u.v6, &b->u.v6, sizeof(a->u.v6)) == 0)
+		return true;
+#endif /* CONFIG_IPV6 */
+
+	return false;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/ip_addr.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/ip_addr.h
new file mode 100644
index 0000000..1d35e0b
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/ip_addr.h
@@ -0,0 +1,29 @@
+/*
+ * IP address processing
+ * Copyright (c) 2003-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef IP_ADDR_H
+#define IP_ADDR_H
+
+struct hostapd_ip_addr {
+	int af; /* AF_INET / AF_INET6 */
+	union {
+		struct in_addr v4;
+#ifdef CONFIG_IPV6
+		struct in6_addr v6;
+#endif /* CONFIG_IPV6 */
+		u8 max_len[16];
+	} u;
+};
+
+const char * hostapd_ip_txt(const struct hostapd_ip_addr *addr, char *buf,
+			    size_t buflen);
+int hostapd_parse_ip_addr(const char *txt, struct hostapd_ip_addr *addr);
+bool hostapd_ip_equal(const struct hostapd_ip_addr *a,
+		      const struct hostapd_ip_addr *b);
+
+#endif /* IP_ADDR_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/json.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/json.c
new file mode 100644
index 0000000..dd12f1b
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/json.c
@@ -0,0 +1,690 @@
+/*
+ * JavaScript Object Notation (JSON) parser (RFC7159)
+ * Copyright (c) 2017, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "base64.h"
+#include "json.h"
+
+#define JSON_MAX_DEPTH 10
+#define JSON_MAX_TOKENS 500
+
+
+void json_escape_string(char *txt, size_t maxlen, const char *data, size_t len)
+{
+	char *end = txt + maxlen;
+	size_t i;
+
+	for (i = 0; i < len; i++) {
+		if (txt + 4 >= end)
+			break;
+
+		switch (data[i]) {
+		case '\"':
+			*txt++ = '\\';
+			*txt++ = '\"';
+			break;
+		case '\\':
+			*txt++ = '\\';
+			*txt++ = '\\';
+			break;
+		case '\n':
+			*txt++ = '\\';
+			*txt++ = 'n';
+			break;
+		case '\r':
+			*txt++ = '\\';
+			*txt++ = 'r';
+			break;
+		case '\t':
+			*txt++ = '\\';
+			*txt++ = 't';
+			break;
+		default:
+			if (data[i] >= 32 && data[i] <= 126) {
+				*txt++ = data[i];
+			} else {
+				txt += os_snprintf(txt, end - txt, "\\u%04x",
+						   (unsigned char) data[i]);
+			}
+			break;
+		}
+	}
+
+	*txt = '\0';
+}
+
+
+static char * json_parse_string(const char **json_pos, const char *end)
+{
+	const char *pos = *json_pos;
+	char *str, *spos, *s_end;
+	size_t max_len, buf_len;
+	u8 bin[2];
+
+	pos++; /* skip starting quote */
+
+	max_len = end - pos + 1;
+	buf_len = max_len > 10 ? 10 : max_len;
+	str = os_malloc(buf_len);
+	if (!str)
+		return NULL;
+	spos = str;
+	s_end = str + buf_len;
+
+	for (; pos < end; pos++) {
+		if (buf_len < max_len && s_end - spos < 3) {
+			char *tmp;
+			int idx;
+
+			idx = spos - str;
+			buf_len *= 2;
+			if (buf_len > max_len)
+				buf_len = max_len;
+			tmp = os_realloc(str, buf_len);
+			if (!tmp)
+				goto fail;
+			str = tmp;
+			spos = str + idx;
+			s_end = str + buf_len;
+		}
+
+		switch (*pos) {
+		case '\"': /* end string */
+			*spos = '\0';
+			/* caller will move to the next position */
+			*json_pos = pos;
+			return str;
+		case '\\':
+			pos++;
+			if (pos >= end) {
+				wpa_printf(MSG_DEBUG,
+					   "JSON: Truncated \\ escape");
+				goto fail;
+			}
+			switch (*pos) {
+			case '"':
+			case '\\':
+			case '/':
+				*spos++ = *pos;
+				break;
+			case 'n':
+				*spos++ = '\n';
+				break;
+			case 'r':
+				*spos++ = '\r';
+				break;
+			case 't':
+				*spos++ = '\t';
+				break;
+			case 'u':
+				if (end - pos < 5 ||
+				    hexstr2bin(pos + 1, bin, 2) < 0 ||
+				    bin[1] == 0x00) {
+					wpa_printf(MSG_DEBUG,
+						   "JSON: Invalid \\u escape");
+					goto fail;
+				}
+				if (bin[0] == 0x00) {
+					*spos++ = bin[1];
+				} else {
+					*spos++ = bin[0];
+					*spos++ = bin[1];
+				}
+				pos += 4;
+				break;
+			default:
+				wpa_printf(MSG_DEBUG,
+					   "JSON: Unknown escape '%c'", *pos);
+				goto fail;
+			}
+			break;
+		default:
+			*spos++ = *pos;
+			break;
+		}
+	}
+
+fail:
+	os_free(str);
+	return NULL;
+}
+
+
+static int json_parse_number(const char **json_pos, const char *end,
+			     int *ret_val)
+{
+	const char *pos = *json_pos;
+	size_t len;
+	char *str;
+
+	for (; pos < end; pos++) {
+		if (*pos != '-' && (*pos < '0' || *pos > '9')) {
+			pos--;
+			break;
+		}
+	}
+	if (pos == end)
+		pos--;
+	if (pos < *json_pos)
+		return -1;
+	len = pos - *json_pos + 1;
+	str = os_malloc(len + 1);
+	if (!str)
+		return -1;
+	os_memcpy(str, *json_pos, len);
+	str[len] = '\0';
+
+	*ret_val = atoi(str);
+	os_free(str);
+	*json_pos = pos;
+	return 0;
+}
+
+
+static int json_check_tree_state(struct json_token *token)
+{
+	if (!token)
+		return 0;
+	if (json_check_tree_state(token->child) < 0 ||
+	    json_check_tree_state(token->sibling) < 0)
+		return -1;
+	if (token->state != JSON_COMPLETED) {
+		wpa_printf(MSG_DEBUG,
+			   "JSON: Unexpected token state %d (name=%s type=%d)",
+			   token->state, token->name ? token->name : "N/A",
+			   token->type);
+		return -1;
+	}
+	return 0;
+}
+
+
+static struct json_token * json_alloc_token(unsigned int *tokens)
+{
+	(*tokens)++;
+	if (*tokens > JSON_MAX_TOKENS) {
+		wpa_printf(MSG_DEBUG, "JSON: Maximum token limit exceeded");
+		return NULL;
+	}
+	return os_zalloc(sizeof(struct json_token));
+}
+
+
+struct json_token * json_parse(const char *data, size_t data_len)
+{
+	struct json_token *root = NULL, *curr_token = NULL, *token = NULL;
+	const char *pos, *end;
+	char *str;
+	int num;
+	unsigned int depth = 0;
+	unsigned int tokens = 0;
+
+	pos = data;
+	end = data + data_len;
+
+	for (; pos < end; pos++) {
+		switch (*pos) {
+		case '[': /* start array */
+		case '{': /* start object */
+			if (!curr_token) {
+				token = json_alloc_token(&tokens);
+				if (!token)
+					goto fail;
+				if (!root)
+					root = token;
+			} else if (curr_token->state == JSON_WAITING_VALUE) {
+				token = curr_token;
+			} else if (curr_token->parent &&
+				   curr_token->parent->type == JSON_ARRAY &&
+				   curr_token->parent->state == JSON_STARTED &&
+				   curr_token->state == JSON_EMPTY) {
+				token = curr_token;
+			} else {
+				wpa_printf(MSG_DEBUG,
+					   "JSON: Invalid state for start array/object");
+				goto fail;
+			}
+			depth++;
+			if (depth > JSON_MAX_DEPTH) {
+				wpa_printf(MSG_DEBUG,
+					   "JSON: Max depth exceeded");
+				goto fail;
+			}
+			token->type = *pos == '[' ? JSON_ARRAY : JSON_OBJECT;
+			token->state = JSON_STARTED;
+			token->child = json_alloc_token(&tokens);
+			if (!token->child)
+				goto fail;
+			curr_token = token->child;
+			curr_token->parent = token;
+			curr_token->state = JSON_EMPTY;
+			break;
+		case ']': /* end array */
+		case '}': /* end object */
+			if (!curr_token || !curr_token->parent ||
+			    curr_token->parent->state != JSON_STARTED) {
+				wpa_printf(MSG_DEBUG,
+					   "JSON: Invalid state for end array/object");
+				goto fail;
+			}
+			depth--;
+			curr_token = curr_token->parent;
+			if ((*pos == ']' &&
+			     curr_token->type != JSON_ARRAY) ||
+			    (*pos == '}' &&
+			     curr_token->type != JSON_OBJECT)) {
+				wpa_printf(MSG_DEBUG,
+					   "JSON: Array/Object mismatch");
+				goto fail;
+			}
+			if (curr_token->child->state == JSON_EMPTY &&
+			    !curr_token->child->child &&
+			    !curr_token->child->sibling) {
+				/* Remove pending child token since the
+				 * array/object was empty. */
+				json_free(curr_token->child);
+				curr_token->child = NULL;
+			}
+			curr_token->state = JSON_COMPLETED;
+			break;
+		case '\"': /* string */
+			str = json_parse_string(&pos, end);
+			if (!str)
+				goto fail;
+			if (!curr_token) {
+				token = json_alloc_token(&tokens);
+				if (!token) {
+					os_free(str);
+					goto fail;
+				}
+				token->type = JSON_STRING;
+				token->string = str;
+				token->state = JSON_COMPLETED;
+			} else if (curr_token->parent &&
+				   curr_token->parent->type == JSON_ARRAY &&
+				   curr_token->parent->state == JSON_STARTED &&
+				   curr_token->state == JSON_EMPTY) {
+				curr_token->string = str;
+				curr_token->state = JSON_COMPLETED;
+				curr_token->type = JSON_STRING;
+				wpa_printf(MSG_MSGDUMP,
+					   "JSON: String value: '%s'",
+					   curr_token->string);
+			} else if (curr_token->state == JSON_EMPTY) {
+				curr_token->type = JSON_VALUE;
+				curr_token->name = str;
+				curr_token->state = JSON_STARTED;
+			} else if (curr_token->state == JSON_WAITING_VALUE) {
+				curr_token->string = str;
+				curr_token->state = JSON_COMPLETED;
+				curr_token->type = JSON_STRING;
+				wpa_printf(MSG_MSGDUMP,
+					   "JSON: String value: '%s' = '%s'",
+					   curr_token->name,
+					   curr_token->string);
+			} else {
+				wpa_printf(MSG_DEBUG,
+					   "JSON: Invalid state for a string");
+				os_free(str);
+				goto fail;
+			}
+			break;
+		case ' ':
+		case '\t':
+		case '\r':
+		case '\n':
+			/* ignore whitespace */
+			break;
+		case ':': /* name/value separator */
+			if (!curr_token || curr_token->state != JSON_STARTED)
+				goto fail;
+			curr_token->state = JSON_WAITING_VALUE;
+			break;
+		case ',': /* member separator */
+			if (!curr_token)
+				goto fail;
+			curr_token->sibling = json_alloc_token(&tokens);
+			if (!curr_token->sibling)
+				goto fail;
+			curr_token->sibling->parent = curr_token->parent;
+			curr_token = curr_token->sibling;
+			curr_token->state = JSON_EMPTY;
+			break;
+		case 't': /* true */
+		case 'f': /* false */
+		case 'n': /* null */
+			if (!((end - pos >= 4 &&
+			       os_strncmp(pos, "true", 4) == 0) ||
+			      (end - pos >= 5 &&
+			       os_strncmp(pos, "false", 5) == 0) ||
+			      (end - pos >= 4 &&
+			       os_strncmp(pos, "null", 4) == 0))) {
+				wpa_printf(MSG_DEBUG,
+					   "JSON: Invalid literal name");
+				goto fail;
+			}
+			if (!curr_token) {
+				token = json_alloc_token(&tokens);
+				if (!token)
+					goto fail;
+				curr_token = token;
+			} else if (curr_token->state == JSON_WAITING_VALUE) {
+				wpa_printf(MSG_MSGDUMP,
+					   "JSON: Literal name: '%s' = %c",
+					   curr_token->name, *pos);
+			} else if (curr_token->parent &&
+				   curr_token->parent->type == JSON_ARRAY &&
+				   curr_token->parent->state == JSON_STARTED &&
+				   curr_token->state == JSON_EMPTY) {
+				wpa_printf(MSG_MSGDUMP,
+					   "JSON: Literal name: %c", *pos);
+			} else {
+				wpa_printf(MSG_DEBUG,
+					   "JSON: Invalid state for a literal name");
+				goto fail;
+			}
+			switch (*pos) {
+			case 't':
+				curr_token->type = JSON_BOOLEAN;
+				curr_token->number = 1;
+				pos += 3;
+				break;
+			case 'f':
+				curr_token->type = JSON_BOOLEAN;
+				curr_token->number = 0;
+				pos += 4;
+				break;
+			case 'n':
+				curr_token->type = JSON_NULL;
+				pos += 3;
+				break;
+			}
+			curr_token->state = JSON_COMPLETED;
+			break;
+		case '-':
+		case '0':
+		case '1':
+		case '2':
+		case '3':
+		case '4':
+		case '5':
+		case '6':
+		case '7':
+		case '8':
+		case '9':
+			/* number */
+			if (json_parse_number(&pos, end, &num) < 0)
+				goto fail;
+			if (!curr_token) {
+				token = json_alloc_token(&tokens);
+				if (!token)
+					goto fail;
+				token->type = JSON_NUMBER;
+				token->number = num;
+				token->state = JSON_COMPLETED;
+			} else if (curr_token->state == JSON_WAITING_VALUE) {
+				curr_token->number = num;
+				curr_token->state = JSON_COMPLETED;
+				curr_token->type = JSON_NUMBER;
+				wpa_printf(MSG_MSGDUMP,
+					   "JSON: Number value: '%s' = '%d'",
+					   curr_token->name,
+					   curr_token->number);
+			} else if (curr_token->parent &&
+				   curr_token->parent->type == JSON_ARRAY &&
+				   curr_token->parent->state == JSON_STARTED &&
+				   curr_token->state == JSON_EMPTY) {
+				curr_token->number = num;
+				curr_token->state = JSON_COMPLETED;
+				curr_token->type = JSON_NUMBER;
+				wpa_printf(MSG_MSGDUMP,
+					   "JSON: Number value: %d",
+					   curr_token->number);
+			} else {
+				wpa_printf(MSG_DEBUG,
+					   "JSON: Invalid state for a number");
+				goto fail;
+			}
+			break;
+		default:
+			wpa_printf(MSG_DEBUG,
+				   "JSON: Unexpected JSON character: %c", *pos);
+			goto fail;
+		}
+
+		if (!root)
+			root = token;
+		if (!curr_token)
+			curr_token = token;
+	}
+
+	if (json_check_tree_state(root) < 0) {
+		wpa_printf(MSG_DEBUG, "JSON: Incomplete token in the tree");
+		goto fail;
+	}
+
+	return root;
+fail:
+	wpa_printf(MSG_DEBUG, "JSON: Parsing failed");
+	json_free(root);
+	return NULL;
+}
+
+
+void json_free(struct json_token *json)
+{
+	if (!json)
+		return;
+	json_free(json->child);
+	json_free(json->sibling);
+	os_free(json->name);
+	os_free(json->string);
+	os_free(json);
+}
+
+
+struct json_token * json_get_member(struct json_token *json, const char *name)
+{
+	struct json_token *token, *ret = NULL;
+
+	if (!json || json->type != JSON_OBJECT)
+		return NULL;
+	/* Return last matching entry */
+	for (token = json->child; token; token = token->sibling) {
+		if (token->name && os_strcmp(token->name, name) == 0)
+			ret = token;
+	}
+	return ret;
+}
+
+
+struct wpabuf * json_get_member_base64url(struct json_token *json,
+					  const char *name)
+{
+	struct json_token *token;
+	unsigned char *buf;
+	size_t buflen;
+	struct wpabuf *ret;
+
+	token = json_get_member(json, name);
+	if (!token || token->type != JSON_STRING)
+		return NULL;
+	buf = base64_url_decode(token->string, os_strlen(token->string),
+				&buflen);
+	if (!buf)
+		return NULL;
+	ret = wpabuf_alloc_ext_data(buf, buflen);
+	if (!ret)
+		os_free(buf);
+
+	return ret;
+}
+
+
+struct wpabuf * json_get_member_base64(struct json_token *json,
+				       const char *name)
+{
+	struct json_token *token;
+	unsigned char *buf;
+	size_t buflen;
+	struct wpabuf *ret;
+
+	token = json_get_member(json, name);
+	if (!token || token->type != JSON_STRING)
+		return NULL;
+	buf = base64_decode(token->string, os_strlen(token->string), &buflen);
+	if (!buf)
+		return NULL;
+	ret = wpabuf_alloc_ext_data(buf, buflen);
+	if (!ret)
+		os_free(buf);
+
+	return ret;
+}
+
+
+static const char * json_type_str(enum json_type type)
+{
+	switch (type) {
+	case JSON_VALUE:
+		return "VALUE";
+	case JSON_OBJECT:
+		return "OBJECT";
+	case JSON_ARRAY:
+		return "ARRAY";
+	case JSON_STRING:
+		return "STRING";
+	case JSON_NUMBER:
+		return "NUMBER";
+	case JSON_BOOLEAN:
+		return "BOOLEAN";
+	case JSON_NULL:
+		return "NULL";
+	}
+	return "??";
+}
+
+
+static void json_print_token(struct json_token *token, int depth,
+			     char *buf, size_t buflen)
+{
+	size_t len;
+	int ret;
+
+	if (!token)
+		return;
+	len = os_strlen(buf);
+	ret = os_snprintf(buf + len, buflen - len, "[%d:%s:%s]",
+			  depth, json_type_str(token->type),
+			  token->name ? token->name : "");
+	if (os_snprintf_error(buflen - len, ret)) {
+		buf[len] = '\0';
+		return;
+	}
+	json_print_token(token->child, depth + 1, buf, buflen);
+	json_print_token(token->sibling, depth, buf, buflen);
+}
+
+
+void json_print_tree(struct json_token *root, char *buf, size_t buflen)
+{
+	buf[0] = '\0';
+	json_print_token(root, 1, buf, buflen);
+}
+
+
+void json_add_int(struct wpabuf *json, const char *name, int val)
+{
+	wpabuf_printf(json, "\"%s\":%d", name, val);
+}
+
+
+void json_add_string(struct wpabuf *json, const char *name, const char *val)
+{
+	wpabuf_printf(json, "\"%s\":\"%s\"", name, val);
+}
+
+
+int json_add_string_escape(struct wpabuf *json, const char *name,
+			   const void *val, size_t len)
+{
+	char *tmp;
+	size_t tmp_len = 6 * len + 1;
+
+	tmp = os_malloc(tmp_len);
+	if (!tmp)
+		return -1;
+	json_escape_string(tmp, tmp_len, val, len);
+	json_add_string(json, name, tmp);
+	bin_clear_free(tmp, tmp_len);
+	return 0;
+}
+
+
+int json_add_base64url(struct wpabuf *json, const char *name, const void *val,
+		       size_t len)
+{
+	char *b64;
+
+	b64 = base64_url_encode(val, len, NULL);
+	if (!b64)
+		return -1;
+	json_add_string(json, name, b64);
+	os_free(b64);
+	return 0;
+}
+
+
+int json_add_base64(struct wpabuf *json, const char *name, const void *val,
+		    size_t len)
+{
+	char *b64;
+
+	b64 = base64_encode_no_lf(val, len, NULL);
+	if (!b64)
+		return -1;
+	json_add_string(json, name, b64);
+	os_free(b64);
+	return 0;
+}
+
+
+void json_start_object(struct wpabuf *json, const char *name)
+{
+	if (name)
+		wpabuf_printf(json, "\"%s\":", name);
+	wpabuf_put_u8(json, '{');
+}
+
+
+void json_end_object(struct wpabuf *json)
+{
+	wpabuf_put_u8(json, '}');
+}
+
+
+void json_start_array(struct wpabuf *json, const char *name)
+{
+	if (name)
+		wpabuf_printf(json, "\"%s\":", name);
+	wpabuf_put_u8(json, '[');
+}
+
+
+void json_end_array(struct wpabuf *json)
+{
+	wpabuf_put_u8(json, ']');
+}
+
+
+void json_value_sep(struct wpabuf *json)
+{
+	wpabuf_put_u8(json, ',');
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/json.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/json.h
new file mode 100644
index 0000000..8448bb0
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/json.h
@@ -0,0 +1,57 @@
+/*
+ * JavaScript Object Notation (JSON) parser (RFC7159)
+ * Copyright (c) 2017, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef JSON_H
+#define JSON_H
+
+struct json_token {
+	enum json_type {
+		JSON_VALUE,
+		JSON_OBJECT,
+		JSON_ARRAY,
+		JSON_STRING,
+		JSON_NUMBER,
+		JSON_BOOLEAN,
+		JSON_NULL,
+	} type;
+	enum json_parsing_state {
+		JSON_EMPTY,
+		JSON_STARTED,
+		JSON_WAITING_VALUE,
+		JSON_COMPLETED,
+	} state;
+	char *name;
+	char *string;
+	int number;
+	struct json_token *parent, *child, *sibling;
+};
+
+void json_escape_string(char *txt, size_t maxlen, const char *data, size_t len);
+struct json_token * json_parse(const char *data, size_t data_len);
+void json_free(struct json_token *json);
+struct json_token * json_get_member(struct json_token *json, const char *name);
+struct wpabuf * json_get_member_base64url(struct json_token *json,
+					  const char *name);
+struct wpabuf * json_get_member_base64(struct json_token *json,
+				       const char *name);
+void json_print_tree(struct json_token *root, char *buf, size_t buflen);
+void json_add_int(struct wpabuf *json, const char *name, int val);
+void json_add_string(struct wpabuf *json, const char *name, const char *val);
+int json_add_string_escape(struct wpabuf *json, const char *name,
+			   const void *val, size_t len);
+int json_add_base64url(struct wpabuf *json, const char *name, const void *val,
+		       size_t len);
+int json_add_base64(struct wpabuf *json, const char *name, const void *val,
+		    size_t len);
+void json_start_object(struct wpabuf *json, const char *name);
+void json_end_object(struct wpabuf *json);
+void json_start_array(struct wpabuf *json, const char *name);
+void json_end_array(struct wpabuf *json);
+void json_value_sep(struct wpabuf *json);
+
+#endif /* JSON_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/list.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/list.h
new file mode 100644
index 0000000..aa62c08
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/list.h
@@ -0,0 +1,97 @@
+/*
+ * Doubly-linked list
+ * Copyright (c) 2009-2019, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef LIST_H
+#define LIST_H
+
+/**
+ * struct dl_list - Doubly-linked list
+ */
+struct dl_list {
+	struct dl_list *next;
+	struct dl_list *prev;
+};
+
+#define DL_LIST_HEAD_INIT(l) { &(l), &(l) }
+
+static inline void dl_list_init(struct dl_list *list)
+{
+	list->next = list;
+	list->prev = list;
+}
+
+static inline void dl_list_add(struct dl_list *list, struct dl_list *item)
+{
+	item->next = list->next;
+	item->prev = list;
+	list->next->prev = item;
+	list->next = item;
+}
+
+static inline void dl_list_add_tail(struct dl_list *list, struct dl_list *item)
+{
+	dl_list_add(list->prev, item);
+}
+
+static inline void dl_list_del(struct dl_list *item)
+{
+	item->next->prev = item->prev;
+	item->prev->next = item->next;
+	item->next = NULL;
+	item->prev = NULL;
+}
+
+static inline int dl_list_empty(const struct dl_list *list)
+{
+	return list->next == list;
+}
+
+static inline unsigned int dl_list_len(const struct dl_list *list)
+{
+	struct dl_list *item;
+	int count = 0;
+	for (item = list->next; item != list; item = item->next)
+		count++;
+	return count;
+}
+
+#ifndef offsetof
+#define offsetof(type, member) ((long) &((type *) 0)->member)
+#endif
+
+#define dl_list_entry(item, type, member) \
+	((type *) ((char *) item - offsetof(type, member)))
+
+#define dl_list_first(list, type, member) \
+	(dl_list_empty((list)) ? NULL : \
+	 dl_list_entry((list)->next, type, member))
+
+#define dl_list_last(list, type, member) \
+	(dl_list_empty((list)) ? NULL : \
+	 dl_list_entry((list)->prev, type, member))
+
+#define dl_list_for_each(item, list, type, member) \
+	for (item = dl_list_entry((list)->next, type, member); \
+	     &item->member != (list); \
+	     item = dl_list_entry(item->member.next, type, member))
+
+#define dl_list_for_each_safe(item, n, list, type, member) \
+	for (item = dl_list_entry((list)->next, type, member), \
+		     n = dl_list_entry(item->member.next, type, member); \
+	     &item->member != (list); \
+	     item = n, n = dl_list_entry(n->member.next, type, member))
+
+#define dl_list_for_each_reverse(item, list, type, member) \
+	for (item = dl_list_entry((list)->prev, type, member); \
+	     &item->member != (list); \
+	     item = dl_list_entry(item->member.prev, type, member))
+
+#define DEFINE_DL_LIST(name) \
+	struct dl_list name = { &(name), &(name) }
+
+#endif /* LIST_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/module_tests.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/module_tests.h
new file mode 100644
index 0000000..3bfe4ad
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/module_tests.h
@@ -0,0 +1,20 @@
+/*
+ * Module tests
+ * Copyright (c) 2014-2015, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef MODULE_TESTS_H
+#define MODULE_TESTS_H
+
+int wpas_module_tests(void);
+int hapd_module_tests(void);
+
+int utils_module_tests(void);
+int wps_module_tests(void);
+int common_module_tests(void);
+int crypto_module_tests(void);
+
+#endif /* MODULE_TESTS_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/os.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/os.h
new file mode 100644
index 0000000..21ba5c3
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/os.h
@@ -0,0 +1,680 @@
+/*
+ * OS specific functions
+ * Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef OS_H
+#define OS_H
+
+typedef long os_time_t;
+
+/**
+ * os_sleep - Sleep (sec, usec)
+ * @sec: Number of seconds to sleep
+ * @usec: Number of microseconds to sleep
+ */
+void os_sleep(os_time_t sec, os_time_t usec);
+
+struct os_time {
+	os_time_t sec;
+	os_time_t usec;
+};
+
+struct os_reltime {
+	os_time_t sec;
+	os_time_t usec;
+};
+
+/**
+ * os_get_time - Get current time (sec, usec)
+ * @t: Pointer to buffer for the time
+ * Returns: 0 on success, -1 on failure
+ */
+int os_get_time(struct os_time *t);
+
+/**
+ * os_get_reltime - Get relative time (sec, usec)
+ * @t: Pointer to buffer for the time
+ * Returns: 0 on success, -1 on failure
+ */
+int os_get_reltime(struct os_reltime *t);
+
+
+/* Helpers for handling struct os_time */
+
+static inline int os_time_before(struct os_time *a, struct os_time *b)
+{
+	return (a->sec < b->sec) ||
+	       (a->sec == b->sec && a->usec < b->usec);
+}
+
+
+static inline void os_time_sub(struct os_time *a, struct os_time *b,
+			       struct os_time *res)
+{
+	res->sec = a->sec - b->sec;
+	res->usec = a->usec - b->usec;
+	if (res->usec < 0) {
+		res->sec--;
+		res->usec += 1000000;
+	}
+}
+
+
+/* Helpers for handling struct os_reltime */
+
+static inline int os_reltime_before(struct os_reltime *a,
+				    struct os_reltime *b)
+{
+	return (a->sec < b->sec) ||
+	       (a->sec == b->sec && a->usec < b->usec);
+}
+
+
+static inline void os_reltime_sub(struct os_reltime *a, struct os_reltime *b,
+				  struct os_reltime *res)
+{
+	res->sec = a->sec - b->sec;
+	res->usec = a->usec - b->usec;
+	if (res->usec < 0) {
+		res->sec--;
+		res->usec += 1000000;
+	}
+}
+
+
+static inline void os_reltime_age(struct os_reltime *start,
+				  struct os_reltime *age)
+{
+	struct os_reltime now;
+
+	os_get_reltime(&now);
+	os_reltime_sub(&now, start, age);
+}
+
+
+static inline int os_reltime_expired(struct os_reltime *now,
+				     struct os_reltime *ts,
+				     os_time_t timeout_secs)
+{
+	struct os_reltime age;
+
+	os_reltime_sub(now, ts, &age);
+	return (age.sec > timeout_secs) ||
+	       (age.sec == timeout_secs && age.usec > 0);
+}
+
+
+static inline int os_reltime_initialized(struct os_reltime *t)
+{
+	return t->sec != 0 || t->usec != 0;
+}
+
+
+/**
+ * os_mktime - Convert broken-down time into seconds since 1970-01-01
+ * @year: Four digit year
+ * @month: Month (1 .. 12)
+ * @day: Day of month (1 .. 31)
+ * @hour: Hour (0 .. 23)
+ * @min: Minute (0 .. 59)
+ * @sec: Second (0 .. 60)
+ * @t: Buffer for returning calendar time representation (seconds since
+ * 1970-01-01 00:00:00)
+ * Returns: 0 on success, -1 on failure
+ *
+ * Note: The result is in seconds from Epoch, i.e., in UTC, not in local time
+ * which is used by POSIX mktime().
+ */
+int os_mktime(int year, int month, int day, int hour, int min, int sec,
+	      os_time_t *t);
+
+struct os_tm {
+	int sec; /* 0..59 or 60 for leap seconds */
+	int min; /* 0..59 */
+	int hour; /* 0..23 */
+	int day; /* 1..31 */
+	int month; /* 1..12 */
+	int year; /* Four digit year */
+};
+
+int os_gmtime(os_time_t t, struct os_tm *tm);
+
+/**
+ * os_daemonize - Run in the background (detach from the controlling terminal)
+ * @pid_file: File name to write the process ID to or %NULL to skip this
+ * Returns: 0 on success, -1 on failure
+ */
+int os_daemonize(const char *pid_file);
+
+/**
+ * os_daemonize_terminate - Stop running in the background (remove pid file)
+ * @pid_file: File name to write the process ID to or %NULL to skip this
+ */
+void os_daemonize_terminate(const char *pid_file);
+
+/**
+ * os_get_random - Get cryptographically strong pseudo random data
+ * @buf: Buffer for pseudo random data
+ * @len: Length of the buffer
+ * Returns: 0 on success, -1 on failure
+ */
+int os_get_random(unsigned char *buf, size_t len);
+
+/**
+ * os_random - Get pseudo random value (not necessarily very strong)
+ * Returns: Pseudo random value
+ */
+unsigned long os_random(void);
+
+/**
+ * os_rel2abs_path - Get an absolute path for a file
+ * @rel_path: Relative path to a file
+ * Returns: Absolute path for the file or %NULL on failure
+ *
+ * This function tries to convert a relative path of a file to an absolute path
+ * in order for the file to be found even if current working directory has
+ * changed. The returned value is allocated and caller is responsible for
+ * freeing it. It is acceptable to just return the same path in an allocated
+ * buffer, e.g., return strdup(rel_path). This function is only used to find
+ * configuration files when os_daemonize() may have changed the current working
+ * directory and relative path would be pointing to a different location.
+ */
+char * os_rel2abs_path(const char *rel_path);
+
+/**
+ * os_program_init - Program initialization (called at start)
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is called when a programs starts. If there are any OS specific
+ * processing that is needed, it can be placed here. It is also acceptable to
+ * just return 0 if not special processing is needed.
+ */
+int os_program_init(void);
+
+/**
+ * os_program_deinit - Program deinitialization (called just before exit)
+ *
+ * This function is called just before a program exists. If there are any OS
+ * specific processing, e.g., freeing resourced allocated in os_program_init(),
+ * it should be done here. It is also acceptable for this function to do
+ * nothing.
+ */
+void os_program_deinit(void);
+
+/**
+ * os_setenv - Set environment variable
+ * @name: Name of the variable
+ * @value: Value to set to the variable
+ * @overwrite: Whether existing variable should be overwritten
+ * Returns: 0 on success, -1 on error
+ *
+ * This function is only used for wpa_cli action scripts. OS wrapper does not
+ * need to implement this if such functionality is not needed.
+ */
+int os_setenv(const char *name, const char *value, int overwrite);
+
+/**
+ * os_unsetenv - Delete environent variable
+ * @name: Name of the variable
+ * Returns: 0 on success, -1 on error
+ *
+ * This function is only used for wpa_cli action scripts. OS wrapper does not
+ * need to implement this if such functionality is not needed.
+ */
+int os_unsetenv(const char *name);
+
+/**
+ * os_readfile - Read a file to an allocated memory buffer
+ * @name: Name of the file to read
+ * @len: For returning the length of the allocated buffer
+ * Returns: Pointer to the allocated buffer or %NULL on failure
+ *
+ * This function allocates memory and reads the given file to this buffer. Both
+ * binary and text files can be read with this function. The caller is
+ * responsible for freeing the returned buffer with os_free().
+ */
+char * os_readfile(const char *name, size_t *len);
+
+/**
+ * os_file_exists - Check whether the specified file exists
+ * @fname: Path and name of the file
+ * Returns: 1 if the file exists or 0 if not
+ */
+int os_file_exists(const char *fname);
+
+/**
+ * os_fdatasync - Sync a file's (for a given stream) state with storage device
+ * @stream: the stream to be flushed
+ * Returns: 0 if the operation succeeded or -1 on failure
+ */
+int os_fdatasync(FILE *stream);
+
+/**
+ * os_zalloc - Allocate and zero memory
+ * @size: Number of bytes to allocate
+ * Returns: Pointer to allocated and zeroed memory or %NULL on failure
+ *
+ * Caller is responsible for freeing the returned buffer with os_free().
+ */
+void * os_zalloc(size_t size);
+
+/**
+ * os_calloc - Allocate and zero memory for an array
+ * @nmemb: Number of members in the array
+ * @size: Number of bytes in each member
+ * Returns: Pointer to allocated and zeroed memory or %NULL on failure
+ *
+ * This function can be used as a wrapper for os_zalloc(nmemb * size) when an
+ * allocation is used for an array. The main benefit over os_zalloc() is in
+ * having an extra check to catch integer overflows in multiplication.
+ *
+ * Caller is responsible for freeing the returned buffer with os_free().
+ */
+static inline void * os_calloc(size_t nmemb, size_t size)
+{
+	if (size && nmemb > (~(size_t) 0) / size)
+		return NULL;
+	return os_zalloc(nmemb * size);
+}
+
+
+/*
+ * The following functions are wrapper for standard ANSI C or POSIX functions.
+ * By default, they are just defined to use the standard function name and no
+ * os_*.c implementation is needed for them. This avoids extra function calls
+ * by allowing the C pre-processor take care of the function name mapping.
+ *
+ * If the target system uses a C library that does not provide these functions,
+ * build_config.h can be used to define the wrappers to use a different
+ * function name. This can be done on function-by-function basis since the
+ * defines here are only used if build_config.h does not define the os_* name.
+ * If needed, os_*.c file can be used to implement the functions that are not
+ * included in the C library on the target system. Alternatively,
+ * OS_NO_C_LIB_DEFINES can be defined to skip all defines here in which case
+ * these functions need to be implemented in os_*.c file for the target system.
+ */
+
+#ifdef OS_NO_C_LIB_DEFINES
+
+/**
+ * os_malloc - Allocate dynamic memory
+ * @size: Size of the buffer to allocate
+ * Returns: Allocated buffer or %NULL on failure
+ *
+ * Caller is responsible for freeing the returned buffer with os_free().
+ */
+void * os_malloc(size_t size);
+
+/**
+ * os_realloc - Re-allocate dynamic memory
+ * @ptr: Old buffer from os_malloc() or os_realloc()
+ * @size: Size of the new buffer
+ * Returns: Allocated buffer or %NULL on failure
+ *
+ * Caller is responsible for freeing the returned buffer with os_free().
+ * If re-allocation fails, %NULL is returned and the original buffer (ptr) is
+ * not freed and caller is still responsible for freeing it.
+ */
+void * os_realloc(void *ptr, size_t size);
+
+/**
+ * os_free - Free dynamic memory
+ * @ptr: Old buffer from os_malloc() or os_realloc(); can be %NULL
+ */
+void os_free(void *ptr);
+
+/**
+ * os_memcpy - Copy memory area
+ * @dest: Destination
+ * @src: Source
+ * @n: Number of bytes to copy
+ * Returns: dest
+ *
+ * The memory areas src and dst must not overlap. os_memmove() can be used with
+ * overlapping memory.
+ */
+void * os_memcpy(void *dest, const void *src, size_t n);
+
+/**
+ * os_memmove - Copy memory area
+ * @dest: Destination
+ * @src: Source
+ * @n: Number of bytes to copy
+ * Returns: dest
+ *
+ * The memory areas src and dst may overlap.
+ */
+void * os_memmove(void *dest, const void *src, size_t n);
+
+/**
+ * os_memset - Fill memory with a constant byte
+ * @s: Memory area to be filled
+ * @c: Constant byte
+ * @n: Number of bytes started from s to fill with c
+ * Returns: s
+ */
+void * os_memset(void *s, int c, size_t n);
+
+/**
+ * os_memcmp - Compare memory areas
+ * @s1: First buffer
+ * @s2: Second buffer
+ * @n: Maximum numbers of octets to compare
+ * Returns: An integer less than, equal to, or greater than zero if s1 is
+ * found to be less than, to match, or be greater than s2. Only first n
+ * characters will be compared.
+ */
+int os_memcmp(const void *s1, const void *s2, size_t n);
+
+/**
+ * os_strdup - Duplicate a string
+ * @s: Source string
+ * Returns: Allocated buffer with the string copied into it or %NULL on failure
+ *
+ * Caller is responsible for freeing the returned buffer with os_free().
+ */
+char * os_strdup(const char *s);
+
+/**
+ * os_strlen - Calculate the length of a string
+ * @s: '\0' terminated string
+ * Returns: Number of characters in s (not counting the '\0' terminator)
+ */
+size_t os_strlen(const char *s);
+
+/**
+ * os_strcasecmp - Compare two strings ignoring case
+ * @s1: First string
+ * @s2: Second string
+ * Returns: An integer less than, equal to, or greater than zero if s1 is
+ * found to be less than, to match, or be greatred than s2
+ */
+int os_strcasecmp(const char *s1, const char *s2);
+
+/**
+ * os_strncasecmp - Compare two strings ignoring case
+ * @s1: First string
+ * @s2: Second string
+ * @n: Maximum numbers of characters to compare
+ * Returns: An integer less than, equal to, or greater than zero if s1 is
+ * found to be less than, to match, or be greater than s2. Only first n
+ * characters will be compared.
+ */
+int os_strncasecmp(const char *s1, const char *s2, size_t n);
+
+/**
+ * os_strchr - Locate the first occurrence of a character in string
+ * @s: String
+ * @c: Character to search for
+ * Returns: Pointer to the matched character or %NULL if not found
+ */
+char * os_strchr(const char *s, int c);
+
+/**
+ * os_strrchr - Locate the last occurrence of a character in string
+ * @s: String
+ * @c: Character to search for
+ * Returns: Pointer to the matched character or %NULL if not found
+ */
+char * os_strrchr(const char *s, int c);
+
+/**
+ * os_strcmp - Compare two strings
+ * @s1: First string
+ * @s2: Second string
+ * Returns: An integer less than, equal to, or greater than zero if s1 is
+ * found to be less than, to match, or be greatred than s2
+ */
+int os_strcmp(const char *s1, const char *s2);
+
+/**
+ * os_strncmp - Compare two strings
+ * @s1: First string
+ * @s2: Second string
+ * @n: Maximum numbers of characters to compare
+ * Returns: An integer less than, equal to, or greater than zero if s1 is
+ * found to be less than, to match, or be greater than s2. Only first n
+ * characters will be compared.
+ */
+int os_strncmp(const char *s1, const char *s2, size_t n);
+
+/**
+ * os_strstr - Locate a substring
+ * @haystack: String (haystack) to search from
+ * @needle: Needle to search from haystack
+ * Returns: Pointer to the beginning of the substring or %NULL if not found
+ */
+char * os_strstr(const char *haystack, const char *needle);
+
+/**
+ * os_snprintf - Print to a memory buffer
+ * @str: Memory buffer to print into
+ * @size: Maximum length of the str buffer
+ * @format: printf format
+ * Returns: Number of characters printed (not including trailing '\0').
+ *
+ * If the output buffer is truncated, number of characters which would have
+ * been written is returned. Since some C libraries return -1 in such a case,
+ * the caller must be prepared on that value, too, to indicate truncation.
+ *
+ * Note: Some C library implementations of snprintf() may not guarantee null
+ * termination in case the output is truncated. The OS wrapper function of
+ * os_snprintf() should provide this guarantee, i.e., to null terminate the
+ * output buffer if a C library version of the function is used and if that
+ * function does not guarantee null termination.
+ *
+ * If the target system does not include snprintf(), see, e.g.,
+ * http://www.ijs.si/software/snprintf/ for an example of a portable
+ * implementation of snprintf.
+ */
+int os_snprintf(char *str, size_t size, const char *format, ...);
+
+#else /* OS_NO_C_LIB_DEFINES */
+
+#ifdef WPA_TRACE
+void * os_malloc(size_t size);
+void * os_realloc(void *ptr, size_t size);
+void os_free(void *ptr);
+char * os_strdup(const char *s);
+#else /* WPA_TRACE */
+#ifndef os_malloc
+#define os_malloc(s) malloc((s))
+#endif
+#ifndef os_realloc
+#define os_realloc(p, s) realloc((p), (s))
+#endif
+#ifndef os_free
+#define os_free(p) free((p))
+#endif
+#ifndef os_strdup
+#ifdef _MSC_VER
+#define os_strdup(s) _strdup(s)
+#else
+#define os_strdup(s) strdup(s)
+#endif
+#endif
+#endif /* WPA_TRACE */
+
+#ifndef os_memcpy
+#define os_memcpy(d, s, n) memcpy((d), (s), (n))
+#endif
+#ifndef os_memmove
+#define os_memmove(d, s, n) memmove((d), (s), (n))
+#endif
+#ifndef os_memset
+#define os_memset(s, c, n) memset(s, c, n)
+#endif
+#ifndef os_memcmp
+#define os_memcmp(s1, s2, n) memcmp((s1), (s2), (n))
+#endif
+
+#ifndef os_strlen
+#define os_strlen(s) strlen(s)
+#endif
+#ifndef os_strcasecmp
+#ifdef _MSC_VER
+#define os_strcasecmp(s1, s2) _stricmp((s1), (s2))
+#else
+#define os_strcasecmp(s1, s2) strcasecmp((s1), (s2))
+#endif
+#endif
+#ifndef os_strncasecmp
+#ifdef _MSC_VER
+#define os_strncasecmp(s1, s2, n) _strnicmp((s1), (s2), (n))
+#else
+#define os_strncasecmp(s1, s2, n) strncasecmp((s1), (s2), (n))
+#endif
+#endif
+#ifndef os_strchr
+#define os_strchr(s, c) strchr((s), (c))
+#endif
+#ifndef os_strcmp
+#define os_strcmp(s1, s2) strcmp((s1), (s2))
+#endif
+#ifndef os_strncmp
+#define os_strncmp(s1, s2, n) strncmp((s1), (s2), (n))
+#endif
+#ifndef os_strrchr
+#define os_strrchr(s, c) strrchr((s), (c))
+#endif
+#ifndef os_strstr
+#define os_strstr(h, n) strstr((h), (n))
+#endif
+
+#ifndef os_snprintf
+#ifdef _MSC_VER
+#define os_snprintf _snprintf
+#else
+#define os_snprintf snprintf
+#endif
+#endif
+
+#endif /* OS_NO_C_LIB_DEFINES */
+
+
+static inline int os_snprintf_error(size_t size, int res)
+{
+	return res < 0 || (unsigned int) res >= size;
+}
+
+
+static inline void * os_realloc_array(void *ptr, size_t nmemb, size_t size)
+{
+	if (size && nmemb > (~(size_t) 0) / size)
+		return NULL;
+	return os_realloc(ptr, nmemb * size);
+}
+
+/**
+ * os_remove_in_array - Remove a member from an array by index
+ * @ptr: Pointer to the array
+ * @nmemb: Current member count of the array
+ * @size: The size per member of the array
+ * @idx: Index of the member to be removed
+ */
+static inline void os_remove_in_array(void *ptr, size_t nmemb, size_t size,
+				      size_t idx)
+{
+	if (idx < nmemb - 1)
+		os_memmove(((unsigned char *) ptr) + idx * size,
+			   ((unsigned char *) ptr) + (idx + 1) * size,
+			   (nmemb - idx - 1) * size);
+}
+
+/**
+ * os_strlcpy - Copy a string with size bound and NUL-termination
+ * @dest: Destination
+ * @src: Source
+ * @siz: Size of the target buffer
+ * Returns: Total length of the target string (length of src) (not including
+ * NUL-termination)
+ *
+ * This function matches in behavior with the strlcpy(3) function in OpenBSD.
+ */
+size_t os_strlcpy(char *dest, const char *src, size_t siz);
+
+/**
+ * os_memcmp_const - Constant time memory comparison
+ * @a: First buffer to compare
+ * @b: Second buffer to compare
+ * @len: Number of octets to compare
+ * Returns: 0 if buffers are equal, non-zero if not
+ *
+ * This function is meant for comparing passwords or hash values where
+ * difference in execution time could provide external observer information
+ * about the location of the difference in the memory buffers. The return value
+ * does not behave like os_memcmp(), i.e., os_memcmp_const() cannot be used to
+ * sort items into a defined order. Unlike os_memcmp(), execution time of
+ * os_memcmp_const() does not depend on the contents of the compared memory
+ * buffers, but only on the total compared length.
+ */
+int os_memcmp_const(const void *a, const void *b, size_t len);
+
+
+/**
+ * os_memdup - Allocate duplicate of passed memory chunk
+ * @src: Source buffer to duplicate
+ * @len: Length of source buffer
+ * Returns: %NULL if allocation failed, copy of src buffer otherwise
+ *
+ * This function allocates a memory block like os_malloc() would, and
+ * copies the given source buffer into it.
+ */
+void * os_memdup(const void *src, size_t len);
+
+/**
+ * os_exec - Execute an external program
+ * @program: Path to the program
+ * @arg: Command line argument string
+ * @wait_completion: Whether to wait until the program execution completes
+ * Returns: 0 on success, -1 on error
+ */
+int os_exec(const char *program, const char *arg, int wait_completion);
+
+
+#ifdef OS_REJECT_C_LIB_FUNCTIONS
+#define malloc OS_DO_NOT_USE_malloc
+#define realloc OS_DO_NOT_USE_realloc
+#define free OS_DO_NOT_USE_free
+#define memcpy OS_DO_NOT_USE_memcpy
+#define memmove OS_DO_NOT_USE_memmove
+#define memset OS_DO_NOT_USE_memset
+#define memcmp OS_DO_NOT_USE_memcmp
+#undef strdup
+#define strdup OS_DO_NOT_USE_strdup
+#define strlen OS_DO_NOT_USE_strlen
+#define strcasecmp OS_DO_NOT_USE_strcasecmp
+#define strncasecmp OS_DO_NOT_USE_strncasecmp
+#undef strchr
+#define strchr OS_DO_NOT_USE_strchr
+#undef strcmp
+#define strcmp OS_DO_NOT_USE_strcmp
+#undef strncmp
+#define strncmp OS_DO_NOT_USE_strncmp
+#undef strncpy
+#define strncpy OS_DO_NOT_USE_strncpy
+#define strrchr OS_DO_NOT_USE_strrchr
+#define strstr OS_DO_NOT_USE_strstr
+#undef snprintf
+#define snprintf OS_DO_NOT_USE_snprintf
+
+#define strcpy OS_DO_NOT_USE_strcpy
+#endif /* OS_REJECT_C_LIB_FUNCTIONS */
+
+
+#if defined(WPA_TRACE_BFD) && defined(CONFIG_TESTING_OPTIONS)
+#define TEST_FAIL() testing_test_fail()
+int testing_test_fail(void);
+extern char wpa_trace_fail_func[256];
+extern unsigned int wpa_trace_fail_after;
+extern char wpa_trace_test_fail_func[256];
+extern unsigned int wpa_trace_test_fail_after;
+#else
+#define TEST_FAIL() 0
+#endif
+
+#endif /* OS_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/os_internal.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/os_internal.c
new file mode 100644
index 0000000..feade6e
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/os_internal.c
@@ -0,0 +1,560 @@
+/*
+ * wpa_supplicant/hostapd / Internal implementation of OS specific functions
+ * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ *
+ * This file is an example of operating system specific  wrapper functions.
+ * This version implements many of the functions internally, so it can be used
+ * to fill in missing functions from the target system C libraries.
+ *
+ * Some of the functions are using standard C library calls in order to keep
+ * this file in working condition to allow the functions to be tested on a
+ * Linux target. Please note that OS_NO_C_LIB_DEFINES needs to be defined for
+ * this file to work correctly. Note that these implementations are only
+ * examples and are not optimized for speed.
+ */
+
+#include "includes.h"
+#include <time.h>
+#include <sys/wait.h>
+
+#undef OS_REJECT_C_LIB_FUNCTIONS
+#include "common.h"
+
+void os_sleep(os_time_t sec, os_time_t usec)
+{
+#if defined(_POSIX_C_SOURCE) && (_POSIX_C_SOURCE >= 200809L)
+	const struct timespec req = { sec, usec * 1000 };
+
+	nanosleep(&req, NULL);
+#else
+	if (sec)
+		sleep(sec);
+	if (usec)
+		usleep(usec);
+#endif
+}
+
+
+int os_get_time(struct os_time *t)
+{
+	int res;
+	struct timeval tv;
+	res = gettimeofday(&tv, NULL);
+	t->sec = tv.tv_sec;
+	t->usec = tv.tv_usec;
+	return res;
+}
+
+
+int os_get_reltime(struct os_reltime *t)
+{
+	int res;
+	struct timeval tv;
+	res = gettimeofday(&tv, NULL);
+	t->sec = tv.tv_sec;
+	t->usec = tv.tv_usec;
+	return res;
+}
+
+
+int os_mktime(int year, int month, int day, int hour, int min, int sec,
+	      os_time_t *t)
+{
+	struct tm tm;
+
+	if (year < 1970 || month < 1 || month > 12 || day < 1 || day > 31 ||
+	    hour < 0 || hour > 23 || min < 0 || min > 59 || sec < 0 ||
+	    sec > 60)
+		return -1;
+
+	os_memset(&tm, 0, sizeof(tm));
+	tm.tm_year = year - 1900;
+	tm.tm_mon = month - 1;
+	tm.tm_mday = day;
+	tm.tm_hour = hour;
+	tm.tm_min = min;
+	tm.tm_sec = sec;
+
+	*t = (os_time_t) mktime(&tm);
+	return 0;
+}
+
+
+int os_gmtime(os_time_t t, struct os_tm *tm)
+{
+	struct tm *tm2;
+	time_t t2 = t;
+
+	tm2 = gmtime(&t2);
+	if (tm2 == NULL)
+		return -1;
+	tm->sec = tm2->tm_sec;
+	tm->min = tm2->tm_min;
+	tm->hour = tm2->tm_hour;
+	tm->day = tm2->tm_mday;
+	tm->month = tm2->tm_mon + 1;
+	tm->year = tm2->tm_year + 1900;
+	return 0;
+}
+
+
+int os_daemonize(const char *pid_file)
+{
+	if (daemon(0, 0)) {
+		wpa_printf(MSG_ERROR, "daemon: %s", strerror(errno));
+		return -1;
+	}
+
+	if (pid_file) {
+		FILE *f = fopen(pid_file, "w");
+		if (f) {
+			fprintf(f, "%u\n", getpid());
+			fclose(f);
+		}
+	}
+
+	return -0;
+}
+
+
+void os_daemonize_terminate(const char *pid_file)
+{
+	if (pid_file)
+		unlink(pid_file);
+}
+
+
+int os_get_random(unsigned char *buf, size_t len)
+{
+	FILE *f;
+	size_t rc;
+
+	f = fopen("/dev/urandom", "rb");
+	if (f == NULL) {
+		printf("Could not open /dev/urandom.\n");
+		return -1;
+	}
+
+	rc = fread(buf, 1, len, f);
+	fclose(f);
+
+	return rc != len ? -1 : 0;
+}
+
+
+unsigned long os_random(void)
+{
+	return random();
+}
+
+
+char * os_rel2abs_path(const char *rel_path)
+{
+	char *buf = NULL, *cwd, *ret;
+	size_t len = 128, cwd_len, rel_len, ret_len;
+
+	if (rel_path[0] == '/')
+		return os_strdup(rel_path);
+
+	for (;;) {
+		buf = os_malloc(len);
+		if (buf == NULL)
+			return NULL;
+		cwd = getcwd(buf, len);
+		if (cwd == NULL) {
+			os_free(buf);
+			if (errno != ERANGE) {
+				return NULL;
+			}
+			len *= 2;
+		} else {
+			break;
+		}
+	}
+
+	cwd_len = os_strlen(cwd);
+	rel_len = os_strlen(rel_path);
+	ret_len = cwd_len + 1 + rel_len + 1;
+	ret = os_malloc(ret_len);
+	if (ret) {
+		os_memcpy(ret, cwd, cwd_len);
+		ret[cwd_len] = '/';
+		os_memcpy(ret + cwd_len + 1, rel_path, rel_len);
+		ret[ret_len - 1] = '\0';
+	}
+	os_free(buf);
+	return ret;
+}
+
+
+int os_program_init(void)
+{
+	return 0;
+}
+
+
+void os_program_deinit(void)
+{
+}
+
+
+int os_setenv(const char *name, const char *value, int overwrite)
+{
+	return setenv(name, value, overwrite);
+}
+
+
+int os_unsetenv(const char *name)
+{
+#if defined(__FreeBSD__) || defined(__NetBSD__)
+	unsetenv(name);
+	return 0;
+#else
+	return unsetenv(name);
+#endif
+}
+
+
+char * os_readfile(const char *name, size_t *len)
+{
+	FILE *f;
+	char *buf;
+
+	f = fopen(name, "rb");
+	if (f == NULL)
+		return NULL;
+
+	fseek(f, 0, SEEK_END);
+	*len = ftell(f);
+	fseek(f, 0, SEEK_SET);
+
+	buf = os_malloc(*len);
+	if (buf == NULL) {
+		fclose(f);
+		return NULL;
+	}
+
+	if (fread(buf, 1, *len, f) != *len) {
+		fclose(f);
+		os_free(buf);
+		return NULL;
+	}
+
+	fclose(f);
+
+	return buf;
+}
+
+
+int os_fdatasync(FILE *stream)
+{
+	return 0;
+}
+
+
+void * os_zalloc(size_t size)
+{
+	void *n = os_malloc(size);
+	if (n)
+		os_memset(n, 0, size);
+	return n;
+}
+
+
+void * os_malloc(size_t size)
+{
+	return malloc(size);
+}
+
+
+void * os_realloc(void *ptr, size_t size)
+{
+	return realloc(ptr, size);
+}
+
+
+void os_free(void *ptr)
+{
+	free(ptr);
+}
+
+
+void * os_memcpy(void *dest, const void *src, size_t n)
+{
+	char *d = dest;
+	const char *s = src;
+	while (n--)
+		*d++ = *s++;
+	return dest;
+}
+
+
+void * os_memmove(void *dest, const void *src, size_t n)
+{
+	if (dest < src)
+		os_memcpy(dest, src, n);
+	else {
+		/* overlapping areas */
+		char *d = (char *) dest + n;
+		const char *s = (const char *) src + n;
+		while (n--)
+			*--d = *--s;
+	}
+	return dest;
+}
+
+
+void * os_memset(void *s, int c, size_t n)
+{
+	char *p = s;
+	while (n--)
+		*p++ = c;
+	return s;
+}
+
+
+int os_memcmp(const void *s1, const void *s2, size_t n)
+{
+	const unsigned char *p1 = s1, *p2 = s2;
+
+	if (n == 0)
+		return 0;
+
+	while (*p1 == *p2) {
+		p1++;
+		p2++;
+		n--;
+		if (n == 0)
+			return 0;
+	}
+
+	return *p1 - *p2;
+}
+
+
+char * os_strdup(const char *s)
+{
+	char *res;
+	size_t len;
+	if (s == NULL)
+		return NULL;
+	len = os_strlen(s);
+	res = os_malloc(len + 1);
+	if (res)
+		os_memcpy(res, s, len + 1);
+	return res;
+}
+
+
+size_t os_strlen(const char *s)
+{
+	const char *p = s;
+	while (*p)
+		p++;
+	return p - s;
+}
+
+
+int os_strcasecmp(const char *s1, const char *s2)
+{
+	/*
+	 * Ignoring case is not required for main functionality, so just use
+	 * the case sensitive version of the function.
+	 */
+	return os_strcmp(s1, s2);
+}
+
+
+int os_strncasecmp(const char *s1, const char *s2, size_t n)
+{
+	/*
+	 * Ignoring case is not required for main functionality, so just use
+	 * the case sensitive version of the function.
+	 */
+	return os_strncmp(s1, s2, n);
+}
+
+
+char * os_strchr(const char *s, int c)
+{
+	while (*s) {
+		if (*s == c)
+			return (char *) s;
+		s++;
+	}
+	return NULL;
+}
+
+
+char * os_strrchr(const char *s, int c)
+{
+	const char *p = s;
+	while (*p)
+		p++;
+	p--;
+	while (p >= s) {
+		if (*p == c)
+			return (char *) p;
+		p--;
+	}
+	return NULL;
+}
+
+
+int os_strcmp(const char *s1, const char *s2)
+{
+	while (*s1 == *s2) {
+		if (*s1 == '\0')
+			break;
+		s1++;
+		s2++;
+	}
+
+	return *s1 - *s2;
+}
+
+
+int os_strncmp(const char *s1, const char *s2, size_t n)
+{
+	if (n == 0)
+		return 0;
+
+	while (*s1 == *s2) {
+		if (*s1 == '\0')
+			break;
+		s1++;
+		s2++;
+		n--;
+		if (n == 0)
+			return 0;
+	}
+
+	return *s1 - *s2;
+}
+
+
+size_t os_strlcpy(char *dest, const char *src, size_t siz)
+{
+	const char *s = src;
+	size_t left = siz;
+
+	if (left) {
+		/* Copy string up to the maximum size of the dest buffer */
+		while (--left != 0) {
+			if ((*dest++ = *s++) == '\0')
+				break;
+		}
+	}
+
+	if (left == 0) {
+		/* Not enough room for the string; force NUL-termination */
+		if (siz != 0)
+			*dest = '\0';
+		while (*s++)
+			; /* determine total src string length */
+	}
+
+	return s - src - 1;
+}
+
+
+int os_memcmp_const(const void *a, const void *b, size_t len)
+{
+	const u8 *aa = a;
+	const u8 *bb = b;
+	size_t i;
+	u8 res;
+
+	for (res = 0, i = 0; i < len; i++)
+		res |= aa[i] ^ bb[i];
+
+	return res;
+}
+
+
+char * os_strstr(const char *haystack, const char *needle)
+{
+	size_t len = os_strlen(needle);
+	while (*haystack) {
+		if (os_strncmp(haystack, needle, len) == 0)
+			return (char *) haystack;
+		haystack++;
+	}
+
+	return NULL;
+}
+
+
+int os_snprintf(char *str, size_t size, const char *format, ...)
+{
+	va_list ap;
+	int ret;
+
+	/* See http://www.ijs.si/software/snprintf/ for portable
+	 * implementation of snprintf.
+	 */
+
+	va_start(ap, format);
+	ret = vsnprintf(str, size, format, ap);
+	va_end(ap);
+	if (size > 0)
+		str[size - 1] = '\0';
+	return ret;
+}
+
+
+int os_exec(const char *program, const char *arg, int wait_completion)
+{
+	pid_t pid;
+	int pid_status;
+
+	pid = fork();
+	if (pid < 0) {
+		wpa_printf(MSG_ERROR, "fork: %s", strerror(errno));
+		return -1;
+	}
+
+	if (pid == 0) {
+		/* run the external command in the child process */
+		const int MAX_ARG = 30;
+		char *_program, *_arg, *pos;
+		char *argv[MAX_ARG + 1];
+		int i;
+
+		_program = os_strdup(program);
+		_arg = os_strdup(arg);
+
+		argv[0] = _program;
+
+		i = 1;
+		pos = _arg;
+		while (i < MAX_ARG && pos && *pos) {
+			while (*pos == ' ')
+				pos++;
+			if (*pos == '\0')
+				break;
+			argv[i++] = pos;
+			pos = os_strchr(pos, ' ');
+			if (pos)
+				*pos++ = '\0';
+		}
+		argv[i] = NULL;
+
+		execv(program, argv);
+		wpa_printf(MSG_ERROR, "execv: %s", strerror(errno));
+		os_free(_program);
+		os_free(_arg);
+		exit(0);
+		return -1;
+	}
+
+	if (wait_completion) {
+		/* wait for the child process to complete in the parent */
+		waitpid(pid, &pid_status, 0);
+	}
+
+	return 0;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/os_none.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/os_none.c
new file mode 100644
index 0000000..5e0a3ad
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/os_none.c
@@ -0,0 +1,248 @@
+/*
+ * wpa_supplicant/hostapd / Empty OS specific functions
+ * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ *
+ * This file can be used as a starting point when adding a new OS target. The
+ * functions here do not really work as-is since they are just empty or only
+ * return an error value. os_internal.c can be used as another starting point
+ * or reference since it has example implementation of many of these functions.
+ */
+
+#include "includes.h"
+
+#include "os.h"
+
+void os_sleep(os_time_t sec, os_time_t usec)
+{
+}
+
+
+int os_get_time(struct os_time *t)
+{
+	return -1;
+}
+
+
+int os_get_reltime(struct os_reltime *t)
+{
+	return -1;
+}
+
+
+int os_mktime(int year, int month, int day, int hour, int min, int sec,
+	      os_time_t *t)
+{
+	return -1;
+}
+
+int os_gmtime(os_time_t t, struct os_tm *tm)
+{
+	return -1;
+}
+
+
+int os_daemonize(const char *pid_file)
+{
+	return -1;
+}
+
+
+void os_daemonize_terminate(const char *pid_file)
+{
+}
+
+
+int os_get_random(unsigned char *buf, size_t len)
+{
+	return -1;
+}
+
+
+unsigned long os_random(void)
+{
+	return 0;
+}
+
+
+char * os_rel2abs_path(const char *rel_path)
+{
+	return NULL; /* strdup(rel_path) can be used here */
+}
+
+
+int os_program_init(void)
+{
+	return 0;
+}
+
+
+void os_program_deinit(void)
+{
+}
+
+
+int os_setenv(const char *name, const char *value, int overwrite)
+{
+	return -1;
+}
+
+
+int os_unsetenv(const char *name)
+{
+	return -1;
+}
+
+
+char * os_readfile(const char *name, size_t *len)
+{
+	return NULL;
+}
+
+
+int os_fdatasync(FILE *stream)
+{
+	return 0;
+}
+
+
+void * os_zalloc(size_t size)
+{
+	return NULL;
+}
+
+
+void * os_memdup(const void *src, size_t n)
+{
+	return NULL;
+}
+
+
+#ifdef OS_NO_C_LIB_DEFINES
+void * os_malloc(size_t size)
+{
+	return NULL;
+}
+
+
+void * os_realloc(void *ptr, size_t size)
+{
+	return NULL;
+}
+
+
+void os_free(void *ptr)
+{
+}
+
+
+void * os_memcpy(void *dest, const void *src, size_t n)
+{
+	return dest;
+}
+
+
+void * os_memmove(void *dest, const void *src, size_t n)
+{
+	return dest;
+}
+
+
+void * os_memset(void *s, int c, size_t n)
+{
+	return s;
+}
+
+
+int os_memcmp(const void *s1, const void *s2, size_t n)
+{
+	return 0;
+}
+
+
+char * os_strdup(const char *s)
+{
+	return NULL;
+}
+
+
+size_t os_strlen(const char *s)
+{
+	return 0;
+}
+
+
+int os_strcasecmp(const char *s1, const char *s2)
+{
+	/*
+	 * Ignoring case is not required for main functionality, so just use
+	 * the case sensitive version of the function.
+	 */
+	return os_strcmp(s1, s2);
+}
+
+
+int os_strncasecmp(const char *s1, const char *s2, size_t n)
+{
+	/*
+	 * Ignoring case is not required for main functionality, so just use
+	 * the case sensitive version of the function.
+	 */
+	return os_strncmp(s1, s2, n);
+}
+
+
+char * os_strchr(const char *s, int c)
+{
+	return NULL;
+}
+
+
+char * os_strrchr(const char *s, int c)
+{
+	return NULL;
+}
+
+
+int os_strcmp(const char *s1, const char *s2)
+{
+	return 0;
+}
+
+
+int os_strncmp(const char *s1, const char *s2, size_t n)
+{
+	return 0;
+}
+
+
+size_t os_strlcpy(char *dest, const char *src, size_t size)
+{
+	return 0;
+}
+
+
+int os_memcmp_const(const void *a, const void *b, size_t len)
+{
+	return 0;
+}
+
+char * os_strstr(const char *haystack, const char *needle)
+{
+	return NULL;
+}
+
+
+int os_snprintf(char *str, size_t size, const char *format, ...)
+{
+	return 0;
+}
+#endif /* OS_NO_C_LIB_DEFINES */
+
+
+int os_exec(const char *program, const char *arg, int wait_completion)
+{
+	return -1;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/os_unix.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/os_unix.c
new file mode 100644
index 0000000..5b026f8
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/os_unix.c
@@ -0,0 +1,841 @@
+/*
+ * OS specific functions for UNIX/POSIX systems
+ * Copyright (c) 2005-2019, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include <time.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+
+#ifdef ANDROID
+#include <sys/capability.h>
+#include <sys/prctl.h>
+#include <private/android_filesystem_config.h>
+#endif /* ANDROID */
+
+#ifdef __MACH__
+#include <CoreServices/CoreServices.h>
+#include <mach/mach.h>
+#include <mach/mach_time.h>
+#endif /* __MACH__ */
+
+#include "os.h"
+#include "common.h"
+
+#ifdef WPA_TRACE
+
+#include "wpa_debug.h"
+#include "trace.h"
+#include "list.h"
+
+static struct dl_list alloc_list = DL_LIST_HEAD_INIT(alloc_list);
+
+#define ALLOC_MAGIC 0xa84ef1b2
+#define FREED_MAGIC 0x67fd487a
+
+struct os_alloc_trace {
+	unsigned int magic;
+	struct dl_list list __attribute__((aligned(16)));
+	size_t len;
+	WPA_TRACE_INFO
+} __attribute__((aligned(16)));
+
+#endif /* WPA_TRACE */
+
+
+void os_sleep(os_time_t sec, os_time_t usec)
+{
+#if defined(_POSIX_C_SOURCE) && (_POSIX_C_SOURCE >= 200809L)
+	const struct timespec req = { sec, usec * 1000 };
+
+	nanosleep(&req, NULL);
+#else
+	if (sec)
+		sleep(sec);
+	if (usec)
+		usleep(usec);
+#endif
+}
+
+
+int os_get_time(struct os_time *t)
+{
+	int res;
+	struct timeval tv;
+	res = gettimeofday(&tv, NULL);
+	t->sec = tv.tv_sec;
+	t->usec = tv.tv_usec;
+	return res;
+}
+
+
+int os_get_reltime(struct os_reltime *t)
+{
+#ifndef __MACH__
+#if defined(CLOCK_BOOTTIME)
+	static clockid_t clock_id = CLOCK_BOOTTIME;
+#elif defined(CLOCK_MONOTONIC)
+	static clockid_t clock_id = CLOCK_MONOTONIC;
+#else
+	static clockid_t clock_id = CLOCK_REALTIME;
+#endif
+	struct timespec ts;
+	int res;
+
+	if (TEST_FAIL())
+		return -1;
+
+	while (1) {
+		res = clock_gettime(clock_id, &ts);
+		if (res == 0) {
+			t->sec = ts.tv_sec;
+			t->usec = ts.tv_nsec / 1000;
+			return 0;
+		}
+		switch (clock_id) {
+#ifdef CLOCK_BOOTTIME
+		case CLOCK_BOOTTIME:
+			clock_id = CLOCK_MONOTONIC;
+			break;
+#endif
+#ifdef CLOCK_MONOTONIC
+		case CLOCK_MONOTONIC:
+			clock_id = CLOCK_REALTIME;
+			break;
+#endif
+		case CLOCK_REALTIME:
+			return -1;
+		}
+	}
+#else /* __MACH__ */
+	uint64_t abstime, nano;
+	static mach_timebase_info_data_t info = { 0, 0 };
+
+	if (!info.denom) {
+		if (mach_timebase_info(&info) != KERN_SUCCESS)
+			return -1;
+	}
+
+	abstime = mach_absolute_time();
+	nano = (abstime * info.numer) / info.denom;
+
+	t->sec = nano / NSEC_PER_SEC;
+	t->usec = (nano - (((uint64_t) t->sec) * NSEC_PER_SEC)) / NSEC_PER_USEC;
+
+	return 0;
+#endif /* __MACH__ */
+}
+
+
+int os_mktime(int year, int month, int day, int hour, int min, int sec,
+	      os_time_t *t)
+{
+	struct tm tm, *tm1;
+	time_t t_local, t1, t2;
+	os_time_t tz_offset;
+
+	if (year < 1970 || month < 1 || month > 12 || day < 1 || day > 31 ||
+	    hour < 0 || hour > 23 || min < 0 || min > 59 || sec < 0 ||
+	    sec > 60)
+		return -1;
+
+	memset(&tm, 0, sizeof(tm));
+	tm.tm_year = year - 1900;
+	tm.tm_mon = month - 1;
+	tm.tm_mday = day;
+	tm.tm_hour = hour;
+	tm.tm_min = min;
+	tm.tm_sec = sec;
+
+	t_local = mktime(&tm);
+
+	/* figure out offset to UTC */
+	tm1 = localtime(&t_local);
+	if (tm1) {
+		t1 = mktime(tm1);
+		tm1 = gmtime(&t_local);
+		if (tm1) {
+			t2 = mktime(tm1);
+			tz_offset = t2 - t1;
+		} else
+			tz_offset = 0;
+	} else
+		tz_offset = 0;
+
+	*t = (os_time_t) t_local - tz_offset;
+	return 0;
+}
+
+
+int os_gmtime(os_time_t t, struct os_tm *tm)
+{
+	struct tm *tm2;
+	time_t t2 = t;
+
+	tm2 = gmtime(&t2);
+	if (tm2 == NULL)
+		return -1;
+	tm->sec = tm2->tm_sec;
+	tm->min = tm2->tm_min;
+	tm->hour = tm2->tm_hour;
+	tm->day = tm2->tm_mday;
+	tm->month = tm2->tm_mon + 1;
+	tm->year = tm2->tm_year + 1900;
+	return 0;
+}
+
+int os_daemonize(const char *pid_file)
+{
+	int pid = 0, i, devnull;
+
+#if defined(__uClinux__) || defined(__sun__)
+	return -1;
+#else /* defined(__uClinux__) || defined(__sun__) */
+
+#ifndef __APPLE__
+	pid = fork();
+	if (pid < 0)
+		return -1;
+#endif
+
+	if (pid > 0) {
+		if (pid_file) {
+			FILE *f = fopen(pid_file, "w");
+			if (f) {
+				fprintf(f, "%u\n", pid);
+				fclose(f);
+			}
+		}
+		_exit(0);
+	}
+
+	if (setsid() < 0)
+		return -1;
+
+	if (chdir("/") < 0)
+		return -1;
+
+	devnull = open("/dev/null", O_RDWR);
+	if (devnull < 0)
+		return -1;
+
+	for (i = 0; i <= STDERR_FILENO; i++)
+		dup2(devnull, i);
+
+	if (devnull > 2)
+		close(devnull);
+
+	return -0;
+#endif /* defined(__uClinux__) || defined(__sun__) */
+}
+
+
+void os_daemonize_terminate(const char *pid_file)
+{
+	if (pid_file)
+		unlink(pid_file);
+}
+
+
+int os_get_random(unsigned char *buf, size_t len)
+{
+#ifdef TEST_FUZZ
+	size_t i;
+
+	for (i = 0; i < len; i++)
+		buf[i] = i & 0xff;
+	return 0;
+#else /* TEST_FUZZ */
+	FILE *f;
+	size_t rc;
+
+	if (TEST_FAIL())
+		return -1;
+
+	f = fopen("/dev/urandom", "rb");
+	if (f == NULL) {
+		printf("Could not open /dev/urandom.\n");
+		return -1;
+	}
+
+	rc = fread(buf, 1, len, f);
+	fclose(f);
+
+	return rc != len ? -1 : 0;
+#endif /* TEST_FUZZ */
+}
+
+
+unsigned long os_random(void)
+{
+	return random();
+}
+
+
+char * os_rel2abs_path(const char *rel_path)
+{
+	char *buf = NULL, *cwd, *ret;
+	size_t len = 128, cwd_len, rel_len, ret_len;
+	int last_errno;
+
+	if (!rel_path)
+		return NULL;
+
+	if (rel_path[0] == '/')
+		return os_strdup(rel_path);
+
+	for (;;) {
+		buf = os_malloc(len);
+		if (buf == NULL)
+			return NULL;
+		cwd = getcwd(buf, len);
+		if (cwd == NULL) {
+			last_errno = errno;
+			os_free(buf);
+			if (last_errno != ERANGE)
+				return NULL;
+			len *= 2;
+			if (len > 2000)
+				return NULL;
+		} else {
+			buf[len - 1] = '\0';
+			break;
+		}
+	}
+
+	cwd_len = os_strlen(cwd);
+	rel_len = os_strlen(rel_path);
+	ret_len = cwd_len + 1 + rel_len + 1;
+	ret = os_malloc(ret_len);
+	if (ret) {
+		os_memcpy(ret, cwd, cwd_len);
+		ret[cwd_len] = '/';
+		os_memcpy(ret + cwd_len + 1, rel_path, rel_len);
+		ret[ret_len - 1] = '\0';
+	}
+	os_free(buf);
+	return ret;
+}
+
+
+int os_program_init(void)
+{
+	unsigned int seed;
+
+#ifdef ANDROID
+	/*
+	 * We ignore errors here since errors are normal if we
+	 * are already running as non-root.
+	 */
+#ifdef ANDROID_SETGROUPS_OVERRIDE
+	gid_t groups[] = { ANDROID_SETGROUPS_OVERRIDE };
+#else /* ANDROID_SETGROUPS_OVERRIDE */
+	gid_t groups[] = { AID_INET, AID_WIFI, AID_KEYSTORE };
+#endif /* ANDROID_SETGROUPS_OVERRIDE */
+	struct __user_cap_header_struct header;
+	struct __user_cap_data_struct cap;
+
+	setgroups(ARRAY_SIZE(groups), groups);
+
+	prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);
+
+	setgid(AID_WIFI);
+	setuid(AID_WIFI);
+
+	header.version = _LINUX_CAPABILITY_VERSION;
+	header.pid = 0;
+	cap.effective = cap.permitted =
+		(1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW);
+	cap.inheritable = 0;
+	capset(&header, &cap);
+#endif /* ANDROID */
+
+	if (os_get_random((unsigned char *) &seed, sizeof(seed)) == 0)
+		srandom(seed);
+
+	return 0;
+}
+
+
+void os_program_deinit(void)
+{
+#ifdef WPA_TRACE
+	struct os_alloc_trace *a;
+	unsigned long total = 0;
+	dl_list_for_each(a, &alloc_list, struct os_alloc_trace, list) {
+		total += a->len;
+		if (a->magic != ALLOC_MAGIC) {
+			wpa_printf(MSG_INFO, "MEMLEAK[%p]: invalid magic 0x%x "
+				   "len %lu",
+				   a, a->magic, (unsigned long) a->len);
+			continue;
+		}
+		wpa_printf(MSG_INFO, "MEMLEAK[%p]: len %lu",
+			   a, (unsigned long) a->len);
+		wpa_trace_dump("memleak", a);
+	}
+	if (total)
+		wpa_printf(MSG_INFO, "MEMLEAK: total %lu bytes",
+			   (unsigned long) total);
+	wpa_trace_deinit();
+#endif /* WPA_TRACE */
+}
+
+
+int os_setenv(const char *name, const char *value, int overwrite)
+{
+	return setenv(name, value, overwrite);
+}
+
+
+int os_unsetenv(const char *name)
+{
+#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__APPLE__) || \
+    defined(__OpenBSD__)
+	unsetenv(name);
+	return 0;
+#else
+	return unsetenv(name);
+#endif
+}
+
+
+char * os_readfile(const char *name, size_t *len)
+{
+	FILE *f;
+	char *buf;
+	long pos;
+
+	f = fopen(name, "rb");
+	if (f == NULL)
+		return NULL;
+
+	if (fseek(f, 0, SEEK_END) < 0 || (pos = ftell(f)) < 0) {
+		fclose(f);
+		return NULL;
+	}
+	*len = pos;
+	if (fseek(f, 0, SEEK_SET) < 0) {
+		fclose(f);
+		return NULL;
+	}
+
+	buf = os_malloc(*len);
+	if (buf == NULL) {
+		fclose(f);
+		return NULL;
+	}
+
+	if (fread(buf, 1, *len, f) != *len) {
+		fclose(f);
+		os_free(buf);
+		return NULL;
+	}
+
+	fclose(f);
+
+	return buf;
+}
+
+
+int os_file_exists(const char *fname)
+{
+	return access(fname, F_OK) == 0;
+}
+
+
+int os_fdatasync(FILE *stream)
+{
+	if (!fflush(stream)) {
+#if defined __FreeBSD__ || defined __linux__
+		return fdatasync(fileno(stream));
+#else /* !__linux__ && !__FreeBSD__ */
+#ifdef F_FULLFSYNC
+		/* OS X does not implement fdatasync(). */
+		return fcntl(fileno(stream), F_FULLFSYNC);
+#else /* F_FULLFSYNC */
+		return fsync(fileno(stream));
+#endif /* F_FULLFSYNC */
+#endif /* __linux__ */
+	}
+
+	return -1;
+}
+
+
+#ifndef WPA_TRACE
+void * os_zalloc(size_t size)
+{
+	return calloc(1, size);
+}
+#endif /* WPA_TRACE */
+
+
+size_t os_strlcpy(char *dest, const char *src, size_t siz)
+{
+	const char *s = src;
+	size_t left = siz;
+
+	if (left) {
+		/* Copy string up to the maximum size of the dest buffer */
+		while (--left != 0) {
+			if ((*dest++ = *s++) == '\0')
+				break;
+		}
+	}
+
+	if (left == 0) {
+		/* Not enough room for the string; force NUL-termination */
+		if (siz != 0)
+			*dest = '\0';
+		while (*s++)
+			; /* determine total src string length */
+	}
+
+	return s - src - 1;
+}
+
+
+int os_memcmp_const(const void *a, const void *b, size_t len)
+{
+	const u8 *aa = a;
+	const u8 *bb = b;
+	size_t i;
+	u8 res;
+
+	for (res = 0, i = 0; i < len; i++)
+		res |= aa[i] ^ bb[i];
+
+	return res;
+}
+
+
+void * os_memdup(const void *src, size_t len)
+{
+	void *r = os_malloc(len);
+
+	if (r && src)
+		os_memcpy(r, src, len);
+	return r;
+}
+
+
+#ifdef WPA_TRACE
+
+#if defined(WPA_TRACE_BFD) && defined(CONFIG_TESTING_OPTIONS)
+char wpa_trace_fail_func[256] = { 0 };
+unsigned int wpa_trace_fail_after;
+
+static int testing_fail_alloc(void)
+{
+	const char *func[WPA_TRACE_LEN];
+	size_t i, res, len;
+	char *pos, *next;
+	int match;
+
+	if (!wpa_trace_fail_after)
+		return 0;
+
+	res = wpa_trace_calling_func(func, WPA_TRACE_LEN);
+	i = 0;
+	if (i < res && os_strcmp(func[i], __func__) == 0)
+		i++;
+	if (i < res && os_strcmp(func[i], "os_malloc") == 0)
+		i++;
+	if (i < res && os_strcmp(func[i], "os_zalloc") == 0)
+		i++;
+	if (i < res && os_strcmp(func[i], "os_calloc") == 0)
+		i++;
+	if (i < res && os_strcmp(func[i], "os_realloc") == 0)
+		i++;
+	if (i < res && os_strcmp(func[i], "os_realloc_array") == 0)
+		i++;
+	if (i < res && os_strcmp(func[i], "os_strdup") == 0)
+		i++;
+	if (i < res && os_strcmp(func[i], "os_memdup") == 0)
+		i++;
+
+	pos = wpa_trace_fail_func;
+
+	match = 0;
+	while (i < res) {
+		int allow_skip = 1;
+		int maybe = 0;
+
+		if (*pos == '=') {
+			allow_skip = 0;
+			pos++;
+		} else if (*pos == '?') {
+			maybe = 1;
+			pos++;
+		}
+		next = os_strchr(pos, ';');
+		if (next)
+			len = next - pos;
+		else
+			len = os_strlen(pos);
+		if (os_memcmp(pos, func[i], len) != 0) {
+			if (maybe && next) {
+				pos = next + 1;
+				continue;
+			}
+			if (allow_skip) {
+				i++;
+				continue;
+			}
+			return 0;
+		}
+		if (!next) {
+			match = 1;
+			break;
+		}
+		pos = next + 1;
+		i++;
+	}
+	if (!match)
+		return 0;
+
+	wpa_trace_fail_after--;
+	if (wpa_trace_fail_after == 0) {
+		wpa_printf(MSG_INFO, "TESTING: fail allocation at %s",
+			   wpa_trace_fail_func);
+		for (i = 0; i < res; i++)
+			wpa_printf(MSG_INFO, "backtrace[%d] = %s",
+				   (int) i, func[i]);
+		return 1;
+	}
+
+	return 0;
+}
+
+
+char wpa_trace_test_fail_func[256] = { 0 };
+unsigned int wpa_trace_test_fail_after;
+
+int testing_test_fail(void)
+{
+	const char *func[WPA_TRACE_LEN];
+	size_t i, res, len;
+	char *pos, *next;
+	int match;
+
+	if (!wpa_trace_test_fail_after)
+		return 0;
+
+	res = wpa_trace_calling_func(func, WPA_TRACE_LEN);
+	i = 0;
+	if (i < res && os_strcmp(func[i], __func__) == 0)
+		i++;
+
+	pos = wpa_trace_test_fail_func;
+
+	match = 0;
+	while (i < res) {
+		int allow_skip = 1;
+		int maybe = 0;
+
+		if (*pos == '=') {
+			allow_skip = 0;
+			pos++;
+		} else if (*pos == '?') {
+			maybe = 1;
+			pos++;
+		}
+		next = os_strchr(pos, ';');
+		if (next)
+			len = next - pos;
+		else
+			len = os_strlen(pos);
+		if (os_memcmp(pos, func[i], len) != 0) {
+			if (maybe && next) {
+				pos = next + 1;
+				continue;
+			}
+			if (allow_skip) {
+				i++;
+				continue;
+			}
+			return 0;
+		}
+		if (!next) {
+			match = 1;
+			break;
+		}
+		pos = next + 1;
+		i++;
+	}
+	if (!match)
+		return 0;
+
+	wpa_trace_test_fail_after--;
+	if (wpa_trace_test_fail_after == 0) {
+		wpa_printf(MSG_INFO, "TESTING: fail at %s",
+			   wpa_trace_test_fail_func);
+		for (i = 0; i < res; i++)
+			wpa_printf(MSG_INFO, "backtrace[%d] = %s",
+				   (int) i, func[i]);
+		return 1;
+	}
+
+	return 0;
+}
+
+#else
+
+static inline int testing_fail_alloc(void)
+{
+	return 0;
+}
+#endif
+
+void * os_malloc(size_t size)
+{
+	struct os_alloc_trace *a;
+
+	if (testing_fail_alloc())
+		return NULL;
+
+	a = malloc(sizeof(*a) + size);
+	if (a == NULL)
+		return NULL;
+	a->magic = ALLOC_MAGIC;
+	dl_list_add(&alloc_list, &a->list);
+	a->len = size;
+	wpa_trace_record(a);
+	return a + 1;
+}
+
+
+void * os_realloc(void *ptr, size_t size)
+{
+	struct os_alloc_trace *a;
+	size_t copy_len;
+	void *n;
+
+	if (ptr == NULL)
+		return os_malloc(size);
+
+	a = (struct os_alloc_trace *) ptr - 1;
+	if (a->magic != ALLOC_MAGIC) {
+		wpa_printf(MSG_INFO, "REALLOC[%p]: invalid magic 0x%x%s",
+			   a, a->magic,
+			   a->magic == FREED_MAGIC ? " (already freed)" : "");
+		wpa_trace_show("Invalid os_realloc() call");
+		abort();
+	}
+	n = os_malloc(size);
+	if (n == NULL)
+		return NULL;
+	copy_len = a->len;
+	if (copy_len > size)
+		copy_len = size;
+	os_memcpy(n, a + 1, copy_len);
+	os_free(ptr);
+	return n;
+}
+
+
+void os_free(void *ptr)
+{
+	struct os_alloc_trace *a;
+
+	if (ptr == NULL)
+		return;
+	a = (struct os_alloc_trace *) ptr - 1;
+	if (a->magic != ALLOC_MAGIC) {
+		wpa_printf(MSG_INFO, "FREE[%p]: invalid magic 0x%x%s",
+			   a, a->magic,
+			   a->magic == FREED_MAGIC ? " (already freed)" : "");
+		wpa_trace_show("Invalid os_free() call");
+		abort();
+	}
+	dl_list_del(&a->list);
+	a->magic = FREED_MAGIC;
+
+	wpa_trace_check_ref(ptr);
+	free(a);
+}
+
+
+void * os_zalloc(size_t size)
+{
+	void *ptr = os_malloc(size);
+	if (ptr)
+		os_memset(ptr, 0, size);
+	return ptr;
+}
+
+
+char * os_strdup(const char *s)
+{
+	size_t len;
+	char *d;
+	len = os_strlen(s);
+	d = os_malloc(len + 1);
+	if (d == NULL)
+		return NULL;
+	os_memcpy(d, s, len);
+	d[len] = '\0';
+	return d;
+}
+
+#endif /* WPA_TRACE */
+
+
+int os_exec(const char *program, const char *arg, int wait_completion)
+{
+	pid_t pid;
+	int pid_status;
+
+	pid = fork();
+	if (pid < 0) {
+		perror("fork");
+		return -1;
+	}
+
+	if (pid == 0) {
+		/* run the external command in the child process */
+		const int MAX_ARG = 30;
+		char *_program, *_arg, *pos;
+		char *argv[MAX_ARG + 1];
+		int i;
+
+		_program = os_strdup(program);
+		_arg = os_strdup(arg);
+
+		argv[0] = _program;
+
+		i = 1;
+		pos = _arg;
+		while (i < MAX_ARG && pos && *pos) {
+			while (*pos == ' ')
+				pos++;
+			if (*pos == '\0')
+				break;
+			argv[i++] = pos;
+			pos = os_strchr(pos, ' ');
+			if (pos)
+				*pos++ = '\0';
+		}
+		argv[i] = NULL;
+
+		execv(program, argv);
+		perror("execv");
+		os_free(_program);
+		os_free(_arg);
+		exit(0);
+		return -1;
+	}
+
+	if (wait_completion) {
+		/* wait for the child process to complete in the parent */
+		waitpid(pid, &pid_status, 0);
+	}
+
+	return 0;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/os_win32.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/os_win32.c
new file mode 100644
index 0000000..f9e4b30
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/os_win32.c
@@ -0,0 +1,295 @@
+/*
+ * wpa_supplicant/hostapd / OS specific functions for Win32 systems
+ * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <time.h>
+#include <winsock2.h>
+#include <wincrypt.h>
+
+#include "os.h"
+#include "common.h"
+
+void os_sleep(os_time_t sec, os_time_t usec)
+{
+	if (sec)
+		Sleep(sec * 1000);
+	if (usec)
+		Sleep(usec / 1000);
+}
+
+
+int os_get_time(struct os_time *t)
+{
+#define EPOCHFILETIME (116444736000000000ULL)
+	FILETIME ft;
+	LARGE_INTEGER li;
+	ULONGLONG tt;
+
+#ifdef _WIN32_WCE
+	SYSTEMTIME st;
+
+	GetSystemTime(&st);
+	SystemTimeToFileTime(&st, &ft);
+#else /* _WIN32_WCE */
+	GetSystemTimeAsFileTime(&ft);
+#endif /* _WIN32_WCE */
+	li.LowPart = ft.dwLowDateTime;
+	li.HighPart = ft.dwHighDateTime;
+	tt = (li.QuadPart - EPOCHFILETIME) / 10;
+	t->sec = (os_time_t) (tt / 1000000);
+	t->usec = (os_time_t) (tt % 1000000);
+
+	return 0;
+}
+
+
+int os_get_reltime(struct os_reltime *t)
+{
+	/* consider using performance counters or so instead */
+	struct os_time now;
+	int res = os_get_time(&now);
+	t->sec = now.sec;
+	t->usec = now.usec;
+	return res;
+}
+
+
+int os_mktime(int year, int month, int day, int hour, int min, int sec,
+	      os_time_t *t)
+{
+	struct tm tm, *tm1;
+	time_t t_local, t1, t2;
+	os_time_t tz_offset;
+
+	if (year < 1970 || month < 1 || month > 12 || day < 1 || day > 31 ||
+	    hour < 0 || hour > 23 || min < 0 || min > 59 || sec < 0 ||
+	    sec > 60)
+		return -1;
+
+	memset(&tm, 0, sizeof(tm));
+	tm.tm_year = year - 1900;
+	tm.tm_mon = month - 1;
+	tm.tm_mday = day;
+	tm.tm_hour = hour;
+	tm.tm_min = min;
+	tm.tm_sec = sec;
+
+	t_local = mktime(&tm);
+
+	/* figure out offset to UTC */
+	tm1 = localtime(&t_local);
+	if (tm1) {
+		t1 = mktime(tm1);
+		tm1 = gmtime(&t_local);
+		if (tm1) {
+			t2 = mktime(tm1);
+			tz_offset = t2 - t1;
+		} else
+			tz_offset = 0;
+	} else
+		tz_offset = 0;
+
+	*t = (os_time_t) t_local - tz_offset;
+	return 0;
+}
+
+
+int os_gmtime(os_time_t t, struct os_tm *tm)
+{
+	struct tm *tm2;
+	time_t t2 = t;
+
+	tm2 = gmtime(&t2);
+	if (tm2 == NULL)
+		return -1;
+	tm->sec = tm2->tm_sec;
+	tm->min = tm2->tm_min;
+	tm->hour = tm2->tm_hour;
+	tm->day = tm2->tm_mday;
+	tm->month = tm2->tm_mon + 1;
+	tm->year = tm2->tm_year + 1900;
+	return 0;
+}
+
+
+int os_daemonize(const char *pid_file)
+{
+	/* TODO */
+	return -1;
+}
+
+
+void os_daemonize_terminate(const char *pid_file)
+{
+}
+
+
+int os_get_random(unsigned char *buf, size_t len)
+{
+	HCRYPTPROV prov;
+	BOOL ret;
+
+	if (!CryptAcquireContext(&prov, NULL, NULL, PROV_RSA_FULL,
+				 CRYPT_VERIFYCONTEXT))
+		return -1;
+
+	ret = CryptGenRandom(prov, len, buf);
+	CryptReleaseContext(prov, 0);
+
+	return ret ? 0 : -1;
+}
+
+
+unsigned long os_random(void)
+{
+	return rand();
+}
+
+
+char * os_rel2abs_path(const char *rel_path)
+{
+	return _strdup(rel_path);
+}
+
+
+int os_program_init(void)
+{
+#ifdef CONFIG_NATIVE_WINDOWS
+	WSADATA wsaData;
+	if (WSAStartup(MAKEWORD(2, 0), &wsaData)) {
+		printf("Could not find a usable WinSock.dll\n");
+		return -1;
+	}
+#endif /* CONFIG_NATIVE_WINDOWS */
+	return 0;
+}
+
+
+void os_program_deinit(void)
+{
+#ifdef CONFIG_NATIVE_WINDOWS
+	WSACleanup();
+#endif /* CONFIG_NATIVE_WINDOWS */
+}
+
+
+int os_setenv(const char *name, const char *value, int overwrite)
+{
+	return -1;
+}
+
+
+int os_unsetenv(const char *name)
+{
+	return -1;
+}
+
+
+char * os_readfile(const char *name, size_t *len)
+{
+	FILE *f;
+	char *buf;
+
+	f = fopen(name, "rb");
+	if (f == NULL)
+		return NULL;
+
+	fseek(f, 0, SEEK_END);
+	*len = ftell(f);
+	fseek(f, 0, SEEK_SET);
+
+	buf = malloc(*len);
+	if (buf == NULL) {
+		fclose(f);
+		return NULL;
+	}
+
+	fread(buf, 1, *len, f);
+	fclose(f);
+
+	return buf;
+}
+
+
+int os_fdatasync(FILE *stream)
+{
+	HANDLE h;
+
+	if (stream == NULL)
+		return -1;
+
+	h = (HANDLE) _get_osfhandle(_fileno(stream));
+	if (h == INVALID_HANDLE_VALUE)
+		return -1;
+
+	if (!FlushFileBuffers(h))
+		return -1;
+
+	return 0;
+}
+
+
+void * os_zalloc(size_t size)
+{
+	return calloc(1, size);
+}
+
+
+size_t os_strlcpy(char *dest, const char *src, size_t siz)
+{
+	const char *s = src;
+	size_t left = siz;
+
+	if (left) {
+		/* Copy string up to the maximum size of the dest buffer */
+		while (--left != 0) {
+			if ((*dest++ = *s++) == '\0')
+				break;
+		}
+	}
+
+	if (left == 0) {
+		/* Not enough room for the string; force NUL-termination */
+		if (siz != 0)
+			*dest = '\0';
+		while (*s++)
+			; /* determine total src string length */
+	}
+
+	return s - src - 1;
+}
+
+
+int os_memcmp_const(const void *a, const void *b, size_t len)
+{
+	const u8 *aa = a;
+	const u8 *bb = b;
+	size_t i;
+	u8 res;
+
+	for (res = 0, i = 0; i < len; i++)
+		res |= aa[i] ^ bb[i];
+
+	return res;
+}
+
+
+int os_exec(const char *program, const char *arg, int wait_completion)
+{
+	return -1;
+}
+
+
+void * os_memdup(const void *src, size_t len)
+{
+	void *r = os_malloc(len);
+
+	if (r)
+		os_memcpy(r, src, len);
+	return r;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/pcsc_funcs.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/pcsc_funcs.c
new file mode 100644
index 0000000..383ed3d
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/pcsc_funcs.c
@@ -0,0 +1,1451 @@
+/*
+ * WPA Supplicant / PC/SC smartcard interface for USIM, GSM SIM
+ * Copyright (c) 2004-2007, 2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ *
+ * This file implements wrapper functions for accessing GSM SIM and 3GPP USIM
+ * cards through PC/SC smartcard library. These functions are used to implement
+ * authentication routines for EAP-SIM and EAP-AKA.
+ */
+
+#include "includes.h"
+#ifdef __APPLE__
+#include <PCSC/winscard.h>
+#else
+#include <winscard.h>
+#endif
+
+#include "common.h"
+#include "pcsc_funcs.h"
+
+
+/* See ETSI GSM 11.11 and ETSI TS 102 221 for details.
+ * SIM commands:
+ * Command APDU: CLA INS P1 P2 P3 Data
+ *   CLA (class of instruction): A0 for GSM, 00 for USIM
+ *   INS (instruction)
+ *   P1 P2 P3 (parameters, P3 = length of Data)
+ * Response APDU: Data SW1 SW2
+ *   SW1 SW2 (Status words)
+ * Commands (INS P1 P2 P3):
+ *   SELECT: A4 00 00 02 <file_id, 2 bytes>
+ *   GET RESPONSE: C0 00 00 <len>
+ *   RUN GSM ALG: 88 00 00 00 <RAND len = 10>
+ *   RUN UMTS ALG: 88 00 81 <len=0x22> data: 0x10 | RAND | 0x10 | AUTN
+ *	P1 = ID of alg in card
+ *	P2 = ID of secret key
+ *   READ BINARY: B0 <offset high> <offset low> <len>
+ *   READ RECORD: B2 <record number> <mode> <len>
+ *	P2 (mode) = '02' (next record), '03' (previous record),
+ *		    '04' (absolute mode)
+ *   VERIFY CHV: 20 00 <CHV number> 08
+ *   CHANGE CHV: 24 00 <CHV number> 10
+ *   DISABLE CHV: 26 00 01 08
+ *   ENABLE CHV: 28 00 01 08
+ *   UNBLOCK CHV: 2C 00 <00=CHV1, 02=CHV2> 10
+ *   SLEEP: FA 00 00 00
+ */
+
+/* GSM SIM commands */
+#define SIM_CMD_SELECT			0xa0, 0xa4, 0x00, 0x00, 0x02
+#define SIM_CMD_RUN_GSM_ALG		0xa0, 0x88, 0x00, 0x00, 0x10
+#define SIM_CMD_GET_RESPONSE		0xa0, 0xc0, 0x00, 0x00
+#define SIM_CMD_READ_BIN		0xa0, 0xb0, 0x00, 0x00
+#define SIM_CMD_READ_RECORD		0xa0, 0xb2, 0x00, 0x00
+#define SIM_CMD_VERIFY_CHV1		0xa0, 0x20, 0x00, 0x01, 0x08
+
+/* USIM commands */
+#define USIM_CLA			0x00
+#define USIM_CMD_RUN_UMTS_ALG		0x00, 0x88, 0x00, 0x81, 0x22
+#define USIM_CMD_GET_RESPONSE		0x00, 0xc0, 0x00, 0x00
+
+#define SIM_RECORD_MODE_ABSOLUTE 0x04
+
+#define USIM_FSP_TEMPL_TAG		0x62
+
+#define USIM_TLV_FILE_DESC		0x82
+#define USIM_TLV_FILE_ID		0x83
+#define USIM_TLV_DF_NAME		0x84
+#define USIM_TLV_PROPR_INFO		0xA5
+#define USIM_TLV_LIFE_CYCLE_STATUS	0x8A
+#define USIM_TLV_FILE_SIZE		0x80
+#define USIM_TLV_TOTAL_FILE_SIZE	0x81
+#define USIM_TLV_PIN_STATUS_TEMPLATE	0xC6
+#define USIM_TLV_SHORT_FILE_ID		0x88
+#define USIM_TLV_SECURITY_ATTR_8B	0x8B
+#define USIM_TLV_SECURITY_ATTR_8C	0x8C
+#define USIM_TLV_SECURITY_ATTR_AB	0xAB
+
+#define USIM_PS_DO_TAG			0x90
+
+#define AKA_RAND_LEN 16
+#define AKA_AUTN_LEN 16
+#define AKA_AUTS_LEN 14
+#define RES_MAX_LEN 16
+#define IK_LEN 16
+#define CK_LEN 16
+
+
+/* GSM files
+ * File type in first octet:
+ * 3F = Master File
+ * 7F = Dedicated File
+ * 2F = Elementary File under the Master File
+ * 6F = Elementary File under a Dedicated File
+ */
+#define SCARD_FILE_MF		0x3F00
+#define SCARD_FILE_GSM_DF	0x7F20
+#define SCARD_FILE_UMTS_DF	0x7F50
+#define SCARD_FILE_GSM_EF_IMSI	0x6F07
+#define SCARD_FILE_GSM_EF_AD	0x6FAD
+#define SCARD_FILE_EF_DIR	0x2F00
+#define SCARD_FILE_EF_ICCID	0x2FE2
+#define SCARD_FILE_EF_CK	0x6FE1
+#define SCARD_FILE_EF_IK	0x6FE2
+
+#define SCARD_CHV1_OFFSET	13
+#define SCARD_CHV1_FLAG		0x80
+
+
+typedef enum { SCARD_GSM_SIM, SCARD_USIM } sim_types;
+
+struct scard_data {
+	SCARDCONTEXT ctx;
+	SCARDHANDLE card;
+#ifdef __APPLE__
+	uint32_t protocol;
+#else
+	DWORD protocol;
+#endif
+	sim_types sim_type;
+	int pin1_required;
+};
+
+#ifdef __MINGW32_VERSION
+/* MinGW does not yet support WinScard, so load the needed functions
+ * dynamically from winscard.dll for now. */
+
+static HINSTANCE dll = NULL; /* winscard.dll */
+
+static const SCARD_IO_REQUEST *dll_g_rgSCardT0Pci, *dll_g_rgSCardT1Pci;
+#undef SCARD_PCI_T0
+#define SCARD_PCI_T0 (dll_g_rgSCardT0Pci)
+#undef SCARD_PCI_T1
+#define SCARD_PCI_T1 (dll_g_rgSCardT1Pci)
+
+
+static WINSCARDAPI LONG WINAPI
+(*dll_SCardEstablishContext)(IN DWORD dwScope,
+			     IN LPCVOID pvReserved1,
+			     IN LPCVOID pvReserved2,
+			     OUT LPSCARDCONTEXT phContext);
+#define SCardEstablishContext dll_SCardEstablishContext
+
+static long (*dll_SCardReleaseContext)(long hContext);
+#define SCardReleaseContext dll_SCardReleaseContext
+
+static WINSCARDAPI LONG WINAPI
+(*dll_SCardListReadersA)(IN SCARDCONTEXT hContext,
+			 IN LPCSTR mszGroups,
+			 OUT LPSTR mszReaders,
+			 IN OUT LPDWORD pcchReaders);
+#undef SCardListReaders
+#define SCardListReaders dll_SCardListReadersA
+
+static WINSCARDAPI LONG WINAPI
+(*dll_SCardConnectA)(IN SCARDCONTEXT hContext,
+		     IN LPCSTR szReader,
+		     IN DWORD dwShareMode,
+		     IN DWORD dwPreferredProtocols,
+		     OUT LPSCARDHANDLE phCard,
+		     OUT LPDWORD pdwActiveProtocol);
+#undef SCardConnect
+#define SCardConnect dll_SCardConnectA
+
+static WINSCARDAPI LONG WINAPI
+(*dll_SCardDisconnect)(IN SCARDHANDLE hCard,
+		       IN DWORD dwDisposition);
+#define SCardDisconnect dll_SCardDisconnect
+
+static WINSCARDAPI LONG WINAPI
+(*dll_SCardTransmit)(IN SCARDHANDLE hCard,
+		     IN LPCSCARD_IO_REQUEST pioSendPci,
+		     IN LPCBYTE pbSendBuffer,
+		     IN DWORD cbSendLength,
+		     IN OUT LPSCARD_IO_REQUEST pioRecvPci,
+		     OUT LPBYTE pbRecvBuffer,
+		     IN OUT LPDWORD pcbRecvLength);
+#define SCardTransmit dll_SCardTransmit
+
+static WINSCARDAPI LONG WINAPI
+(*dll_SCardBeginTransaction)(IN SCARDHANDLE hCard);
+#define SCardBeginTransaction dll_SCardBeginTransaction
+
+static WINSCARDAPI LONG WINAPI
+(*dll_SCardEndTransaction)(IN SCARDHANDLE hCard, IN DWORD dwDisposition);
+#define SCardEndTransaction dll_SCardEndTransaction
+
+
+static int mingw_load_symbols(void)
+{
+	char *sym;
+
+	if (dll)
+		return 0;
+
+	dll = LoadLibrary("winscard");
+	if (dll == NULL) {
+		wpa_printf(MSG_DEBUG, "WinSCard: Could not load winscard.dll "
+			   "library");
+		return -1;
+	}
+
+#define LOADSYM(s) \
+	sym = #s; \
+	dll_ ## s = (void *) GetProcAddress(dll, sym); \
+	if (dll_ ## s == NULL) \
+		goto fail;
+
+	LOADSYM(SCardEstablishContext);
+	LOADSYM(SCardReleaseContext);
+	LOADSYM(SCardListReadersA);
+	LOADSYM(SCardConnectA);
+	LOADSYM(SCardDisconnect);
+	LOADSYM(SCardTransmit);
+	LOADSYM(SCardBeginTransaction);
+	LOADSYM(SCardEndTransaction);
+	LOADSYM(g_rgSCardT0Pci);
+	LOADSYM(g_rgSCardT1Pci);
+
+#undef LOADSYM
+
+	return 0;
+
+fail:
+	wpa_printf(MSG_DEBUG, "WinSCard: Could not get address for %s from "
+		   "winscard.dll", sym);
+	FreeLibrary(dll);
+	dll = NULL;
+	return -1;
+}
+
+
+static void mingw_unload_symbols(void)
+{
+	if (dll == NULL)
+		return;
+
+	FreeLibrary(dll);
+	dll = NULL;
+}
+
+#else /* __MINGW32_VERSION */
+
+#define mingw_load_symbols() 0
+#define mingw_unload_symbols() do { } while (0)
+
+#endif /* __MINGW32_VERSION */
+
+
+static int _scard_select_file(struct scard_data *scard, unsigned short file_id,
+			      unsigned char *buf, size_t *buf_len,
+			      sim_types sim_type, unsigned char *aid,
+			      size_t aidlen);
+static int scard_select_file(struct scard_data *scard, unsigned short file_id,
+			     unsigned char *buf, size_t *buf_len);
+static int scard_verify_pin(struct scard_data *scard, const char *pin);
+static int scard_get_record_len(struct scard_data *scard,
+				unsigned char recnum, unsigned char mode);
+static int scard_read_record(struct scard_data *scard,
+			     unsigned char *data, size_t len,
+			     unsigned char recnum, unsigned char mode);
+
+
+static int scard_parse_fsp_templ(unsigned char *buf, size_t buf_len,
+				 int *ps_do, int *file_len)
+{
+	unsigned char *pos, *end;
+
+	if (ps_do)
+		*ps_do = -1;
+	if (file_len)
+		*file_len = -1;
+
+	pos = buf;
+	end = pos + buf_len;
+	if (*pos != USIM_FSP_TEMPL_TAG) {
+		wpa_printf(MSG_DEBUG, "SCARD: file header did not "
+			   "start with FSP template tag");
+		return -1;
+	}
+	pos++;
+	if (pos >= end)
+		return -1;
+	if (pos[0] < end - pos)
+		end = pos + 1 + pos[0];
+	pos++;
+	wpa_hexdump(MSG_DEBUG, "SCARD: file header FSP template",
+		    pos, end - pos);
+
+	while (end - pos >= 2) {
+		unsigned char type, len;
+
+		type = pos[0];
+		len = pos[1];
+		wpa_printf(MSG_MSGDUMP, "SCARD: file header TLV 0x%02x len=%d",
+			   type, len);
+		pos += 2;
+
+		if (len > (unsigned int) (end - pos))
+			break;
+
+		switch (type) {
+		case USIM_TLV_FILE_DESC:
+			wpa_hexdump(MSG_MSGDUMP, "SCARD: File Descriptor TLV",
+				    pos, len);
+			break;
+		case USIM_TLV_FILE_ID:
+			wpa_hexdump(MSG_MSGDUMP, "SCARD: File Identifier TLV",
+				    pos, len);
+			break;
+		case USIM_TLV_DF_NAME:
+			wpa_hexdump(MSG_MSGDUMP, "SCARD: DF name (AID) TLV",
+				    pos, len);
+			break;
+		case USIM_TLV_PROPR_INFO:
+			wpa_hexdump(MSG_MSGDUMP, "SCARD: Proprietary "
+				    "information TLV", pos, len);
+			break;
+		case USIM_TLV_LIFE_CYCLE_STATUS:
+			wpa_hexdump(MSG_MSGDUMP, "SCARD: Life Cycle Status "
+				    "Integer TLV", pos, len);
+			break;
+		case USIM_TLV_FILE_SIZE:
+			wpa_hexdump(MSG_MSGDUMP, "SCARD: File size TLV",
+				    pos, len);
+			if ((len == 1 || len == 2) && file_len) {
+				if (len == 1)
+					*file_len = (int) pos[0];
+				else
+					*file_len = WPA_GET_BE16(pos);
+				wpa_printf(MSG_DEBUG, "SCARD: file_size=%d",
+					   *file_len);
+			}
+			break;
+		case USIM_TLV_TOTAL_FILE_SIZE:
+			wpa_hexdump(MSG_MSGDUMP, "SCARD: Total file size TLV",
+				    pos, len);
+			break;
+		case USIM_TLV_PIN_STATUS_TEMPLATE:
+			wpa_hexdump(MSG_MSGDUMP, "SCARD: PIN Status Template "
+				    "DO TLV", pos, len);
+			if (len >= 2 && pos[0] == USIM_PS_DO_TAG &&
+			    pos[1] >= 1 && ps_do) {
+				wpa_printf(MSG_DEBUG, "SCARD: PS_DO=0x%02x",
+					   pos[2]);
+				*ps_do = (int) pos[2];
+			}
+			break;
+		case USIM_TLV_SHORT_FILE_ID:
+			wpa_hexdump(MSG_MSGDUMP, "SCARD: Short File "
+				    "Identifier (SFI) TLV", pos, len);
+			break;
+		case USIM_TLV_SECURITY_ATTR_8B:
+		case USIM_TLV_SECURITY_ATTR_8C:
+		case USIM_TLV_SECURITY_ATTR_AB:
+			wpa_hexdump(MSG_MSGDUMP, "SCARD: Security attribute "
+				    "TLV", pos, len);
+			break;
+		default:
+			wpa_hexdump(MSG_MSGDUMP, "SCARD: Unrecognized TLV",
+				    pos, len);
+			break;
+		}
+
+		pos += len;
+
+		if (pos == end)
+			return 0;
+	}
+	return -1;
+}
+
+
+static int scard_pin_needed(struct scard_data *scard,
+			    unsigned char *hdr, size_t hlen)
+{
+	if (scard->sim_type == SCARD_GSM_SIM) {
+		if (hlen > SCARD_CHV1_OFFSET &&
+		    !(hdr[SCARD_CHV1_OFFSET] & SCARD_CHV1_FLAG))
+			return 1;
+		return 0;
+	}
+
+	if (scard->sim_type == SCARD_USIM) {
+		int ps_do;
+		if (scard_parse_fsp_templ(hdr, hlen, &ps_do, NULL))
+			return -1;
+		/* TODO: there could be more than one PS_DO entry because of
+		 * multiple PINs in key reference.. */
+		if (ps_do > 0 && (ps_do & 0x80))
+			return 1;
+		return 0;
+	}
+
+	return -1;
+}
+
+
+static int scard_get_aid(struct scard_data *scard, unsigned char *aid,
+			 size_t maxlen)
+{
+	int rlen, rec;
+	struct efdir {
+		unsigned char appl_template_tag; /* 0x61 */
+		unsigned char appl_template_len;
+		unsigned char appl_id_tag; /* 0x4f */
+		unsigned char aid_len;
+		unsigned char rid[5];
+		unsigned char appl_code[2]; /* 0x1002 for 3G USIM */
+	} *efdir;
+	unsigned char buf[127], *aid_pos;
+	size_t blen;
+	unsigned int aid_len = 0;
+
+	efdir = (struct efdir *) buf;
+	aid_pos = &buf[4];
+	blen = sizeof(buf);
+	if (scard_select_file(scard, SCARD_FILE_EF_DIR, buf, &blen)) {
+		wpa_printf(MSG_DEBUG, "SCARD: Failed to read EF_DIR");
+		return -1;
+	}
+	wpa_hexdump(MSG_DEBUG, "SCARD: EF_DIR select", buf, blen);
+
+	for (rec = 1; rec < 10; rec++) {
+		rlen = scard_get_record_len(scard, rec,
+					    SIM_RECORD_MODE_ABSOLUTE);
+		if (rlen < 0) {
+			wpa_printf(MSG_DEBUG, "SCARD: Failed to get EF_DIR "
+				   "record length");
+			return -1;
+		}
+		blen = sizeof(buf);
+		if (rlen > (int) blen) {
+			wpa_printf(MSG_DEBUG, "SCARD: Too long EF_DIR record");
+			return -1;
+		}
+		if (scard_read_record(scard, buf, rlen, rec,
+				      SIM_RECORD_MODE_ABSOLUTE) < 0) {
+			wpa_printf(MSG_DEBUG, "SCARD: Failed to read "
+				   "EF_DIR record %d", rec);
+			return -1;
+		}
+		wpa_hexdump(MSG_DEBUG, "SCARD: EF_DIR record", buf, rlen);
+
+		if (efdir->appl_template_tag != 0x61) {
+			wpa_printf(MSG_DEBUG, "SCARD: Unexpected application "
+				   "template tag 0x%x",
+				   efdir->appl_template_tag);
+			continue;
+		}
+
+		if (efdir->appl_template_len > rlen - 2) {
+			wpa_printf(MSG_DEBUG, "SCARD: Too long application "
+				   "template (len=%d rlen=%d)",
+				   efdir->appl_template_len, rlen);
+			continue;
+		}
+
+		if (efdir->appl_id_tag != 0x4f) {
+			wpa_printf(MSG_DEBUG, "SCARD: Unexpected application "
+				   "identifier tag 0x%x", efdir->appl_id_tag);
+			continue;
+		}
+
+		aid_len = efdir->aid_len;
+		if (aid_len < 1 || aid_len > 16) {
+			wpa_printf(MSG_DEBUG, "SCARD: Invalid AID length %u",
+				   aid_len);
+			continue;
+		}
+
+		wpa_hexdump(MSG_DEBUG, "SCARD: AID from EF_DIR record",
+			    aid_pos, aid_len);
+
+		if (efdir->appl_code[0] == 0x10 &&
+		    efdir->appl_code[1] == 0x02) {
+			wpa_printf(MSG_DEBUG, "SCARD: 3G USIM app found from "
+				   "EF_DIR record %d", rec);
+			break;
+		}
+	}
+
+	if (rec >= 10) {
+		wpa_printf(MSG_DEBUG, "SCARD: 3G USIM app not found "
+			   "from EF_DIR records");
+		return -1;
+	}
+
+	if (aid_len > maxlen) {
+		wpa_printf(MSG_DEBUG, "SCARD: Too long AID");
+		return -1;
+	}
+
+	os_memcpy(aid, aid_pos, aid_len);
+
+	return aid_len;
+}
+
+
+/**
+ * scard_init - Initialize SIM/USIM connection using PC/SC
+ * @reader: Reader name prefix to search for
+ * Returns: Pointer to private data structure, or %NULL on failure
+ *
+ * This function is used to initialize SIM/USIM connection. PC/SC is used to
+ * open connection to the SIM/USIM card. In addition, local flag is set if a
+ * PIN is needed to access some of the card functions. Once the connection is
+ * not needed anymore, scard_deinit() can be used to close it.
+ */
+struct scard_data * scard_init(const char *reader)
+{
+	long ret;
+#ifdef __APPLE__
+	uint32_t len;
+#else
+	unsigned long len;
+#endif
+	unsigned long pos;
+	struct scard_data *scard;
+#ifdef CONFIG_NATIVE_WINDOWS
+	TCHAR *readers = NULL;
+#else /* CONFIG_NATIVE_WINDOWS */
+	char *readers = NULL;
+#endif /* CONFIG_NATIVE_WINDOWS */
+	unsigned char buf[100];
+	size_t blen;
+	int transaction = 0;
+	int pin_needed;
+
+	wpa_printf(MSG_DEBUG, "SCARD: initializing smart card interface");
+	if (mingw_load_symbols())
+		return NULL;
+	scard = os_zalloc(sizeof(*scard));
+	if (scard == NULL)
+		return NULL;
+
+	ret = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL,
+				    &scard->ctx);
+	if (ret != SCARD_S_SUCCESS) {
+		wpa_printf(MSG_DEBUG, "SCARD: Could not establish smart card "
+			   "context (err=%ld)", ret);
+		goto failed;
+	}
+
+	ret = SCardListReaders(scard->ctx, NULL, NULL, &len);
+	if (ret != SCARD_S_SUCCESS) {
+		wpa_printf(MSG_DEBUG, "SCARD: SCardListReaders failed "
+			   "(err=%ld)", ret);
+		goto failed;
+	}
+
+#ifdef UNICODE
+	len *= 2;
+#endif /* UNICODE */
+	readers = os_malloc(len);
+	if (readers == NULL) {
+		wpa_printf(MSG_INFO, "SCARD: malloc failed\n");
+		goto failed;
+	}
+
+	ret = SCardListReaders(scard->ctx, NULL, readers, &len);
+	if (ret != SCARD_S_SUCCESS) {
+		wpa_printf(MSG_DEBUG, "SCARD: SCardListReaders failed(2) "
+			   "(err=%ld)", ret);
+		goto failed;
+	}
+	if (len < 3) {
+		wpa_printf(MSG_WARNING, "SCARD: No smart card readers "
+			   "available.");
+		goto failed;
+	}
+	wpa_hexdump_ascii(MSG_DEBUG, "SCARD: Readers", (u8 *) readers, len);
+	/*
+	 * readers is a list of available readers. The last entry is terminated
+	 * with double null.
+	 */
+	pos = 0;
+#ifdef UNICODE
+	/* TODO */
+#else /* UNICODE */
+	while (pos < len) {
+		if (reader == NULL ||
+		    os_strncmp(&readers[pos], reader, os_strlen(reader)) == 0)
+			break;
+		while (pos < len && readers[pos])
+			pos++;
+		pos++; /* skip separating null */
+		if (pos < len && readers[pos] == '\0')
+			pos = len; /* double null terminates list */
+	}
+#endif /* UNICODE */
+	if (pos >= len) {
+		wpa_printf(MSG_WARNING, "SCARD: No reader with prefix '%s' "
+			   "found", reader);
+		goto failed;
+	}
+
+#ifdef UNICODE
+	wpa_printf(MSG_DEBUG, "SCARD: Selected reader='%S'", &readers[pos]);
+#else /* UNICODE */
+	wpa_printf(MSG_DEBUG, "SCARD: Selected reader='%s'", &readers[pos]);
+#endif /* UNICODE */
+
+	ret = SCardConnect(scard->ctx, &readers[pos], SCARD_SHARE_SHARED,
+			   SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1,
+			   &scard->card, &scard->protocol);
+	if (ret != SCARD_S_SUCCESS) {
+		if (ret == (long) SCARD_E_NO_SMARTCARD)
+			wpa_printf(MSG_INFO, "No smart card inserted.");
+		else
+			wpa_printf(MSG_WARNING, "SCardConnect err=%lx", ret);
+		goto failed;
+	}
+
+	os_free(readers);
+	readers = NULL;
+
+	wpa_printf(MSG_DEBUG, "SCARD: card=0x%x active_protocol=%lu (%s)",
+		   (unsigned int) scard->card, (unsigned long) scard->protocol,
+		   scard->protocol == SCARD_PROTOCOL_T0 ? "T0" : "T1");
+
+	ret = SCardBeginTransaction(scard->card);
+	if (ret != SCARD_S_SUCCESS) {
+		wpa_printf(MSG_DEBUG, "SCARD: Could not begin transaction: "
+			   "0x%x", (unsigned int) ret);
+		goto failed;
+	}
+	transaction = 1;
+
+	blen = sizeof(buf);
+
+	wpa_printf(MSG_DEBUG, "SCARD: verifying USIM support");
+	if (_scard_select_file(scard, SCARD_FILE_MF, buf, &blen,
+			       SCARD_USIM, NULL, 0)) {
+		wpa_printf(MSG_DEBUG, "SCARD: USIM is not supported. Trying to use GSM SIM");
+		scard->sim_type = SCARD_GSM_SIM;
+	} else {
+		wpa_printf(MSG_DEBUG, "SCARD: USIM is supported");
+		scard->sim_type = SCARD_USIM;
+	}
+
+	if (scard->sim_type == SCARD_GSM_SIM) {
+		blen = sizeof(buf);
+		if (scard_select_file(scard, SCARD_FILE_MF, buf, &blen)) {
+			wpa_printf(MSG_DEBUG, "SCARD: Failed to read MF");
+			goto failed;
+		}
+
+		blen = sizeof(buf);
+		if (scard_select_file(scard, SCARD_FILE_GSM_DF, buf, &blen)) {
+			wpa_printf(MSG_DEBUG, "SCARD: Failed to read GSM DF");
+			goto failed;
+		}
+	} else {
+		unsigned char aid[32];
+		int aid_len;
+
+		aid_len = scard_get_aid(scard, aid, sizeof(aid));
+		if (aid_len < 0) {
+			wpa_printf(MSG_DEBUG, "SCARD: Failed to find AID for "
+				   "3G USIM app - try to use standard 3G RID");
+			os_memcpy(aid, "\xa0\x00\x00\x00\x87", 5);
+			aid_len = 5;
+		}
+		wpa_hexdump(MSG_DEBUG, "SCARD: 3G USIM AID", aid, aid_len);
+
+		/* Select based on AID = 3G RID from EF_DIR. This is usually
+		 * starting with A0 00 00 00 87. */
+		blen = sizeof(buf);
+		if (_scard_select_file(scard, 0, buf, &blen, scard->sim_type,
+				       aid, aid_len)) {
+			wpa_printf(MSG_INFO, "SCARD: Failed to read 3G USIM "
+				   "app");
+			wpa_hexdump(MSG_INFO, "SCARD: 3G USIM AID",
+				    aid, aid_len);
+			goto failed;
+		}
+	}
+
+	/* Verify whether CHV1 (PIN1) is needed to access the card. */
+	pin_needed = scard_pin_needed(scard, buf, blen);
+	if (pin_needed < 0) {
+		wpa_printf(MSG_DEBUG, "SCARD: Failed to determine whether PIN "
+			   "is needed");
+		goto failed;
+	}
+	if (pin_needed) {
+		scard->pin1_required = 1;
+		wpa_printf(MSG_DEBUG, "PIN1 needed for SIM access (retry "
+			   "counter=%d)", scard_get_pin_retry_counter(scard));
+	}
+
+	ret = SCardEndTransaction(scard->card, SCARD_LEAVE_CARD);
+	if (ret != SCARD_S_SUCCESS) {
+		wpa_printf(MSG_DEBUG, "SCARD: Could not end transaction: "
+			   "0x%x", (unsigned int) ret);
+	}
+
+	return scard;
+
+failed:
+	if (transaction)
+		SCardEndTransaction(scard->card, SCARD_LEAVE_CARD);
+	os_free(readers);
+	scard_deinit(scard);
+	return NULL;
+}
+
+
+/**
+ * scard_set_pin - Set PIN (CHV1/PIN1) code for accessing SIM/USIM commands
+ * @scard: Pointer to private data from scard_init()
+ * @pin: PIN code as an ASCII string (e.g., "1234")
+ * Returns: 0 on success, -1 on failure
+ */
+int scard_set_pin(struct scard_data *scard, const char *pin)
+{
+	if (scard == NULL)
+		return -1;
+
+	/* Verify whether CHV1 (PIN1) is needed to access the card. */
+	if (scard->pin1_required) {
+		if (pin == NULL) {
+			wpa_printf(MSG_DEBUG, "No PIN configured for SIM "
+				   "access");
+			return -1;
+		}
+		if (scard_verify_pin(scard, pin)) {
+			wpa_printf(MSG_INFO, "PIN verification failed for "
+				"SIM access");
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+
+/**
+ * scard_deinit - Deinitialize SIM/USIM connection
+ * @scard: Pointer to private data from scard_init()
+ *
+ * This function closes the SIM/USIM connect opened with scard_init().
+ */
+void scard_deinit(struct scard_data *scard)
+{
+	long ret;
+
+	if (scard == NULL)
+		return;
+
+	wpa_printf(MSG_DEBUG, "SCARD: deinitializing smart card interface");
+	if (scard->card) {
+		ret = SCardDisconnect(scard->card, SCARD_UNPOWER_CARD);
+		if (ret != SCARD_S_SUCCESS) {
+			wpa_printf(MSG_DEBUG, "SCARD: Failed to disconnect "
+				   "smart card (err=%ld)", ret);
+		}
+	}
+
+	if (scard->ctx) {
+		ret = SCardReleaseContext(scard->ctx);
+		if (ret != SCARD_S_SUCCESS) {
+			wpa_printf(MSG_DEBUG, "Failed to release smart card "
+				   "context (err=%ld)", ret);
+		}
+	}
+	os_free(scard);
+	mingw_unload_symbols();
+}
+
+
+static long scard_transmit(struct scard_data *scard,
+			   unsigned char *_send, size_t send_len,
+			   unsigned char *_recv, size_t *recv_len)
+{
+	long ret;
+#ifdef __APPLE__
+	uint32_t rlen;
+#else
+	unsigned long rlen;
+#endif
+
+	wpa_hexdump_key(MSG_DEBUG, "SCARD: scard_transmit: send",
+			_send, send_len);
+	rlen = *recv_len;
+	ret = SCardTransmit(scard->card,
+			    scard->protocol == SCARD_PROTOCOL_T1 ?
+			    SCARD_PCI_T1 : SCARD_PCI_T0,
+			    _send, (unsigned long) send_len,
+			    NULL, _recv, &rlen);
+	*recv_len = rlen;
+	if (ret == SCARD_S_SUCCESS) {
+		wpa_hexdump(MSG_DEBUG, "SCARD: scard_transmit: recv",
+			    _recv, rlen);
+	} else {
+		wpa_printf(MSG_WARNING, "SCARD: SCardTransmit failed "
+			   "(err=0x%lx)", ret);
+	}
+	return ret;
+}
+
+
+static int _scard_select_file(struct scard_data *scard, unsigned short file_id,
+			      unsigned char *buf, size_t *buf_len,
+			      sim_types sim_type, unsigned char *aid,
+			      size_t aidlen)
+{
+	long ret;
+	unsigned char resp[3];
+	unsigned char cmd[50] = { SIM_CMD_SELECT };
+	int cmdlen;
+	unsigned char get_resp[5] = { SIM_CMD_GET_RESPONSE };
+	size_t len, rlen;
+
+	if (sim_type == SCARD_USIM) {
+		cmd[0] = USIM_CLA;
+		cmd[3] = 0x04;
+		get_resp[0] = USIM_CLA;
+	}
+
+	wpa_printf(MSG_DEBUG, "SCARD: select file %04x", file_id);
+	if (aid) {
+		wpa_hexdump(MSG_DEBUG, "SCARD: select file by AID",
+			    aid, aidlen);
+		if (5 + aidlen > sizeof(cmd))
+			return -1;
+		cmd[2] = 0x04; /* Select by AID */
+		cmd[4] = aidlen; /* len */
+		os_memcpy(cmd + 5, aid, aidlen);
+		cmdlen = 5 + aidlen;
+	} else {
+		cmd[5] = file_id >> 8;
+		cmd[6] = file_id & 0xff;
+		cmdlen = 7;
+	}
+	len = sizeof(resp);
+	ret = scard_transmit(scard, cmd, cmdlen, resp, &len);
+	if (ret != SCARD_S_SUCCESS) {
+		wpa_printf(MSG_WARNING, "SCARD: SCardTransmit failed "
+			   "(err=0x%lx)", ret);
+		return -1;
+	}
+
+	if (len != 2) {
+		wpa_printf(MSG_WARNING, "SCARD: unexpected resp len "
+			   "%d (expected 2)", (int) len);
+		return -1;
+	}
+
+	if (resp[0] == 0x98 && resp[1] == 0x04) {
+		/* Security status not satisfied (PIN_WLAN) */
+		wpa_printf(MSG_WARNING, "SCARD: Security status not satisfied "
+			   "(PIN_WLAN)");
+		return -1;
+	}
+
+	if (resp[0] == 0x6e) {
+		wpa_printf(MSG_DEBUG, "SCARD: used CLA not supported");
+		return -1;
+	}
+
+	if (resp[0] != 0x6c && resp[0] != 0x9f && resp[0] != 0x61) {
+		wpa_printf(MSG_WARNING, "SCARD: unexpected response 0x%02x "
+			   "(expected 0x61, 0x6c, or 0x9f)", resp[0]);
+		return -1;
+	}
+	/* Normal ending of command; resp[1] bytes available */
+	get_resp[4] = resp[1];
+	wpa_printf(MSG_DEBUG, "SCARD: trying to get response (%d bytes)",
+		   resp[1]);
+
+	rlen = *buf_len;
+	ret = scard_transmit(scard, get_resp, sizeof(get_resp), buf, &rlen);
+	if (ret == SCARD_S_SUCCESS) {
+		*buf_len = resp[1] < rlen ? resp[1] : rlen;
+		return 0;
+	}
+
+	wpa_printf(MSG_WARNING, "SCARD: SCardTransmit err=0x%lx\n", ret);
+	return -1;
+}
+
+
+static int scard_select_file(struct scard_data *scard, unsigned short file_id,
+			     unsigned char *buf, size_t *buf_len)
+{
+	return _scard_select_file(scard, file_id, buf, buf_len,
+				  scard->sim_type, NULL, 0);
+}
+
+
+static int scard_get_record_len(struct scard_data *scard, unsigned char recnum,
+				unsigned char mode)
+{
+	unsigned char buf[255];
+	unsigned char cmd[5] = { SIM_CMD_READ_RECORD /* , len */ };
+	size_t blen;
+	long ret;
+
+	if (scard->sim_type == SCARD_USIM)
+		cmd[0] = USIM_CLA;
+	cmd[2] = recnum;
+	cmd[3] = mode;
+	cmd[4] = sizeof(buf);
+
+	blen = sizeof(buf);
+	ret = scard_transmit(scard, cmd, sizeof(cmd), buf, &blen);
+	if (ret != SCARD_S_SUCCESS) {
+		wpa_printf(MSG_DEBUG, "SCARD: failed to determine file "
+			   "length for record %d", recnum);
+		return -1;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "SCARD: file length determination response",
+		    buf, blen);
+
+	if (blen < 2 || (buf[0] != 0x6c && buf[0] != 0x67)) {
+		wpa_printf(MSG_DEBUG, "SCARD: unexpected response to file "
+			   "length determination");
+		return -1;
+	}
+
+	return buf[1];
+}
+
+
+static int scard_read_record(struct scard_data *scard,
+			     unsigned char *data, size_t len,
+			     unsigned char recnum, unsigned char mode)
+{
+	unsigned char cmd[5] = { SIM_CMD_READ_RECORD /* , len */ };
+	size_t blen = len + 3;
+	unsigned char *buf;
+	long ret;
+
+	if (scard->sim_type == SCARD_USIM)
+		cmd[0] = USIM_CLA;
+	cmd[2] = recnum;
+	cmd[3] = mode;
+	cmd[4] = len;
+
+	buf = os_malloc(blen);
+	if (buf == NULL)
+		return -1;
+
+	ret = scard_transmit(scard, cmd, sizeof(cmd), buf, &blen);
+	if (ret != SCARD_S_SUCCESS) {
+		os_free(buf);
+		return -2;
+	}
+	if (blen != len + 2) {
+		wpa_printf(MSG_DEBUG, "SCARD: record read returned unexpected "
+			   "length %ld (expected %ld)",
+			   (long) blen, (long) len + 2);
+		os_free(buf);
+		return -3;
+	}
+
+	if (buf[len] != 0x90 || buf[len + 1] != 0x00) {
+		wpa_printf(MSG_DEBUG, "SCARD: record read returned unexpected "
+			   "status %02x %02x (expected 90 00)",
+			   buf[len], buf[len + 1]);
+		os_free(buf);
+		return -4;
+	}
+
+	os_memcpy(data, buf, len);
+	os_free(buf);
+
+	return 0;
+}
+
+
+static int scard_read_file(struct scard_data *scard,
+			   unsigned char *data, size_t len)
+{
+	unsigned char cmd[5] = { SIM_CMD_READ_BIN /* , len */ };
+	size_t blen = len + 3;
+	unsigned char *buf;
+	long ret;
+
+	cmd[4] = len;
+
+	buf = os_malloc(blen);
+	if (buf == NULL)
+		return -1;
+
+	if (scard->sim_type == SCARD_USIM)
+		cmd[0] = USIM_CLA;
+	ret = scard_transmit(scard, cmd, sizeof(cmd), buf, &blen);
+	if (ret != SCARD_S_SUCCESS) {
+		os_free(buf);
+		return -2;
+	}
+	if (blen != len + 2) {
+		wpa_printf(MSG_DEBUG, "SCARD: file read returned unexpected "
+			   "length %ld (expected %ld)",
+			   (long) blen, (long) len + 2);
+		os_free(buf);
+		return -3;
+	}
+
+	if (buf[len] != 0x90 || buf[len + 1] != 0x00) {
+		wpa_printf(MSG_DEBUG, "SCARD: file read returned unexpected "
+			   "status %02x %02x (expected 90 00)",
+			   buf[len], buf[len + 1]);
+		os_free(buf);
+		return -4;
+	}
+
+	os_memcpy(data, buf, len);
+	os_free(buf);
+
+	return 0;
+}
+
+
+static int scard_verify_pin(struct scard_data *scard, const char *pin)
+{
+	long ret;
+	unsigned char resp[3];
+	unsigned char cmd[5 + 8] = { SIM_CMD_VERIFY_CHV1 };
+	size_t len;
+
+	wpa_printf(MSG_DEBUG, "SCARD: verifying PIN");
+
+	if (pin == NULL || os_strlen(pin) > 8)
+		return -1;
+
+	if (scard->sim_type == SCARD_USIM)
+		cmd[0] = USIM_CLA;
+	os_memcpy(cmd + 5, pin, os_strlen(pin));
+	os_memset(cmd + 5 + os_strlen(pin), 0xff, 8 - os_strlen(pin));
+
+	len = sizeof(resp);
+	ret = scard_transmit(scard, cmd, sizeof(cmd), resp, &len);
+	if (ret != SCARD_S_SUCCESS)
+		return -2;
+
+	if (len != 2 || resp[0] != 0x90 || resp[1] != 0x00) {
+		wpa_printf(MSG_WARNING, "SCARD: PIN verification failed");
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "SCARD: PIN verified successfully");
+	return 0;
+}
+
+
+int scard_get_pin_retry_counter(struct scard_data *scard)
+{
+	long ret;
+	unsigned char resp[3];
+	unsigned char cmd[5] = { SIM_CMD_VERIFY_CHV1 };
+	size_t len;
+	u16 val;
+
+	wpa_printf(MSG_DEBUG, "SCARD: fetching PIN retry counter");
+
+	if (scard->sim_type == SCARD_USIM)
+		cmd[0] = USIM_CLA;
+	cmd[4] = 0; /* Empty data */
+
+	len = sizeof(resp);
+	ret = scard_transmit(scard, cmd, sizeof(cmd), resp, &len);
+	if (ret != SCARD_S_SUCCESS)
+		return -2;
+
+	if (len != 2) {
+		wpa_printf(MSG_WARNING, "SCARD: failed to fetch PIN retry "
+			   "counter");
+		return -1;
+	}
+
+	val = WPA_GET_BE16(resp);
+	if (val == 0x63c0 || val == 0x6983) {
+		wpa_printf(MSG_DEBUG, "SCARD: PIN has been blocked");
+		return 0;
+	}
+
+	if (val >= 0x63c0 && val <= 0x63cf)
+		return val & 0x000f;
+
+	wpa_printf(MSG_DEBUG, "SCARD: Unexpected PIN retry counter response "
+		   "value 0x%x", val);
+	return 0;
+}
+
+
+/**
+ * scard_get_imsi - Read IMSI from SIM/USIM card
+ * @scard: Pointer to private data from scard_init()
+ * @imsi: Buffer for IMSI
+ * @len: Length of imsi buffer; set to IMSI length on success
+ * Returns: 0 on success, -1 if IMSI file cannot be selected, -2 if IMSI file
+ * selection returns invalid result code, -3 if parsing FSP template file fails
+ * (USIM only), -4 if IMSI does not fit in the provided imsi buffer (len is set
+ * to needed length), -5 if reading IMSI file fails.
+ *
+ * This function can be used to read IMSI from the SIM/USIM card. If the IMSI
+ * file is PIN protected, scard_set_pin() must have been used to set the
+ * correct PIN code before calling scard_get_imsi().
+ */
+int scard_get_imsi(struct scard_data *scard, char *imsi, size_t *len)
+{
+	unsigned char buf[100];
+	size_t blen, imsilen, i;
+	char *pos;
+
+	wpa_printf(MSG_DEBUG, "SCARD: reading IMSI from (GSM) EF-IMSI");
+	blen = sizeof(buf);
+	if (scard_select_file(scard, SCARD_FILE_GSM_EF_IMSI, buf, &blen))
+		return -1;
+	if (blen < 4) {
+		wpa_printf(MSG_WARNING, "SCARD: too short (GSM) EF-IMSI "
+			   "header (len=%ld)", (long) blen);
+		return -2;
+	}
+
+	if (scard->sim_type == SCARD_GSM_SIM) {
+		blen = WPA_GET_BE16(&buf[2]);
+	} else {
+		int file_size;
+		if (scard_parse_fsp_templ(buf, blen, NULL, &file_size))
+			return -3;
+		blen = file_size;
+	}
+	if (blen < 2 || blen > sizeof(buf)) {
+		wpa_printf(MSG_DEBUG, "SCARD: invalid IMSI file length=%ld",
+			   (long) blen);
+		return -3;
+	}
+
+	imsilen = (blen - 2) * 2 + 1;
+	wpa_printf(MSG_DEBUG, "SCARD: IMSI file length=%ld imsilen=%ld",
+		   (long) blen, (long) imsilen);
+	if (blen < 2 || imsilen > *len) {
+		*len = imsilen;
+		return -4;
+	}
+
+	if (scard_read_file(scard, buf, blen))
+		return -5;
+
+	pos = imsi;
+	*pos++ = '0' + (buf[1] >> 4 & 0x0f);
+	for (i = 2; i < blen; i++) {
+		unsigned char digit;
+
+		digit = buf[i] & 0x0f;
+		if (digit < 10)
+			*pos++ = '0' + digit;
+		else
+			imsilen--;
+
+		digit = buf[i] >> 4 & 0x0f;
+		if (digit < 10)
+			*pos++ = '0' + digit;
+		else
+			imsilen--;
+	}
+	*len = imsilen;
+
+	return 0;
+}
+
+
+/**
+ * scard_get_mnc_len - Read length of MNC in the IMSI from SIM/USIM card
+ * @scard: Pointer to private data from scard_init()
+ * Returns: length (>0) on success, -1 if administrative data file cannot be
+ * selected, -2 if administrative data file selection returns invalid result
+ * code, -3 if parsing FSP template file fails (USIM only), -4 if length of
+ * the file is unexpected, -5 if reading file fails, -6 if MNC length is not
+ * in range (i.e. 2 or 3), -7 if MNC length is not available.
+ *
+ */
+int scard_get_mnc_len(struct scard_data *scard)
+{
+	unsigned char buf[100];
+	size_t blen;
+	int file_size;
+
+	wpa_printf(MSG_DEBUG, "SCARD: reading MNC len from (GSM) EF-AD");
+	blen = sizeof(buf);
+	if (scard_select_file(scard, SCARD_FILE_GSM_EF_AD, buf, &blen))
+		return -1;
+	if (blen < 4) {
+		wpa_printf(MSG_WARNING, "SCARD: too short (GSM) EF-AD "
+			   "header (len=%ld)", (long) blen);
+		return -2;
+	}
+
+	if (scard->sim_type == SCARD_GSM_SIM) {
+		file_size = WPA_GET_BE16(&buf[2]);
+	} else {
+		if (scard_parse_fsp_templ(buf, blen, NULL, &file_size))
+			return -3;
+	}
+	if (file_size == 3) {
+		wpa_printf(MSG_DEBUG, "SCARD: MNC length not available");
+		return -7;
+	}
+	if (file_size < 4 || file_size > (int) sizeof(buf)) {
+		wpa_printf(MSG_DEBUG, "SCARD: invalid file length=%ld",
+			   (long) file_size);
+		return -4;
+	}
+
+	if (scard_read_file(scard, buf, file_size))
+		return -5;
+	buf[3] = buf[3] & 0x0f; /* upper nibble reserved for future use  */
+	if (buf[3] < 2 || buf[3] > 3) {
+		wpa_printf(MSG_DEBUG, "SCARD: invalid MNC length=%ld",
+			   (long) buf[3]);
+		return -6;
+	}
+	wpa_printf(MSG_DEBUG, "SCARD: MNC length=%ld", (long) buf[3]);
+	return buf[3];
+}
+
+
+/**
+ * scard_gsm_auth - Run GSM authentication command on SIM card
+ * @scard: Pointer to private data from scard_init()
+ * @_rand: 16-byte RAND value from HLR/AuC
+ * @sres: 4-byte buffer for SRES
+ * @kc: 8-byte buffer for Kc
+ * Returns: 0 on success, -1 if SIM/USIM connection has not been initialized,
+ * -2 if authentication command execution fails, -3 if unknown response code
+ * for authentication command is received, -4 if reading of response fails,
+ * -5 if if response data is of unexpected length
+ *
+ * This function performs GSM authentication using SIM/USIM card and the
+ * provided RAND value from HLR/AuC. If authentication command can be completed
+ * successfully, SRES and Kc values will be written into sres and kc buffers.
+ */
+int scard_gsm_auth(struct scard_data *scard, const unsigned char *_rand,
+		   unsigned char *sres, unsigned char *kc)
+{
+	unsigned char cmd[5 + 1 + 16] = { SIM_CMD_RUN_GSM_ALG };
+	int cmdlen;
+	unsigned char get_resp[5] = { SIM_CMD_GET_RESPONSE };
+	unsigned char resp[3], buf[12 + 3 + 2];
+	size_t len;
+	long ret;
+
+	if (scard == NULL)
+		return -1;
+
+	wpa_hexdump(MSG_DEBUG, "SCARD: GSM auth - RAND", _rand, 16);
+	if (scard->sim_type == SCARD_GSM_SIM) {
+		cmdlen = 5 + 16;
+		os_memcpy(cmd + 5, _rand, 16);
+	} else {
+		cmdlen = 5 + 1 + 16;
+		cmd[0] = USIM_CLA;
+		cmd[3] = 0x80;
+		cmd[4] = 17;
+		cmd[5] = 16;
+		os_memcpy(cmd + 6, _rand, 16);
+		get_resp[0] = USIM_CLA;
+	}
+	len = sizeof(resp);
+	ret = scard_transmit(scard, cmd, cmdlen, resp, &len);
+	if (ret != SCARD_S_SUCCESS)
+		return -2;
+
+	if ((scard->sim_type == SCARD_GSM_SIM &&
+	     (len != 2 || resp[0] != 0x9f || resp[1] != 0x0c)) ||
+	    (scard->sim_type == SCARD_USIM &&
+	     (len != 2 || resp[0] != 0x61 || resp[1] != 0x0e))) {
+		wpa_printf(MSG_WARNING, "SCARD: unexpected response for GSM "
+			   "auth request (len=%ld resp=%02x %02x)",
+			   (long) len, resp[0], resp[1]);
+		return -3;
+	}
+	get_resp[4] = resp[1];
+
+	len = sizeof(buf);
+	ret = scard_transmit(scard, get_resp, sizeof(get_resp), buf, &len);
+	if (ret != SCARD_S_SUCCESS)
+		return -4;
+
+	if (scard->sim_type == SCARD_GSM_SIM) {
+		if (len != 4 + 8 + 2) {
+			wpa_printf(MSG_WARNING, "SCARD: unexpected data "
+				   "length for GSM auth (len=%ld, expected 14)",
+				   (long) len);
+			return -5;
+		}
+		os_memcpy(sres, buf, 4);
+		os_memcpy(kc, buf + 4, 8);
+	} else {
+		if (len != 1 + 4 + 1 + 8 + 2) {
+			wpa_printf(MSG_WARNING, "SCARD: unexpected data "
+				   "length for USIM auth (len=%ld, "
+				   "expected 16)", (long) len);
+			return -5;
+		}
+		if (buf[0] != 4 || buf[5] != 8) {
+			wpa_printf(MSG_WARNING, "SCARD: unexpected SREC/Kc "
+				   "length (%d %d, expected 4 8)",
+				   buf[0], buf[5]);
+		}
+		os_memcpy(sres, buf + 1, 4);
+		os_memcpy(kc, buf + 6, 8);
+	}
+
+	wpa_hexdump(MSG_DEBUG, "SCARD: GSM auth - SRES", sres, 4);
+	wpa_hexdump(MSG_DEBUG, "SCARD: GSM auth - Kc", kc, 8);
+
+	return 0;
+}
+
+
+/**
+ * scard_umts_auth - Run UMTS authentication command on USIM card
+ * @scard: Pointer to private data from scard_init()
+ * @_rand: 16-byte RAND value from HLR/AuC
+ * @autn: 16-byte AUTN value from HLR/AuC
+ * @res: 16-byte buffer for RES
+ * @res_len: Variable that will be set to RES length
+ * @ik: 16-byte buffer for IK
+ * @ck: 16-byte buffer for CK
+ * @auts: 14-byte buffer for AUTS
+ * Returns: 0 on success, -1 on failure, or -2 if USIM reports synchronization
+ * failure
+ *
+ * This function performs AKA authentication using USIM card and the provided
+ * RAND and AUTN values from HLR/AuC. If authentication command can be
+ * completed successfully, RES, IK, and CK values will be written into provided
+ * buffers and res_len is set to length of received RES value. If USIM reports
+ * synchronization failure, the received AUTS value will be written into auts
+ * buffer. In this case, RES, IK, and CK are not valid.
+ */
+int scard_umts_auth(struct scard_data *scard, const unsigned char *_rand,
+		    const unsigned char *autn,
+		    unsigned char *res, size_t *res_len,
+		    unsigned char *ik, unsigned char *ck, unsigned char *auts)
+{
+	unsigned char cmd[5 + 1 + AKA_RAND_LEN + 1 + AKA_AUTN_LEN] =
+		{ USIM_CMD_RUN_UMTS_ALG };
+	unsigned char get_resp[5] = { USIM_CMD_GET_RESPONSE };
+	unsigned char resp[3], buf[64], *pos, *end;
+	size_t len;
+	long ret;
+
+	if (scard == NULL)
+		return -1;
+
+	if (scard->sim_type == SCARD_GSM_SIM) {
+		wpa_printf(MSG_ERROR, "SCARD: Non-USIM card - cannot do UMTS "
+			   "auth");
+		return -1;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "SCARD: UMTS auth - RAND", _rand, AKA_RAND_LEN);
+	wpa_hexdump(MSG_DEBUG, "SCARD: UMTS auth - AUTN", autn, AKA_AUTN_LEN);
+	cmd[5] = AKA_RAND_LEN;
+	os_memcpy(cmd + 6, _rand, AKA_RAND_LEN);
+	cmd[6 + AKA_RAND_LEN] = AKA_AUTN_LEN;
+	os_memcpy(cmd + 6 + AKA_RAND_LEN + 1, autn, AKA_AUTN_LEN);
+
+	len = sizeof(resp);
+	ret = scard_transmit(scard, cmd, sizeof(cmd), resp, &len);
+	if (ret != SCARD_S_SUCCESS)
+		return -1;
+
+	if (len <= sizeof(resp))
+		wpa_hexdump(MSG_DEBUG, "SCARD: UMTS alg response", resp, len);
+
+	if (len == 2 && resp[0] == 0x98 && resp[1] == 0x62) {
+		wpa_printf(MSG_WARNING, "SCARD: UMTS auth failed - "
+			   "MAC != XMAC");
+		return -1;
+	} else if (len != 2 || resp[0] != 0x61) {
+		wpa_printf(MSG_WARNING, "SCARD: unexpected response for UMTS "
+			   "auth request (len=%ld resp=%02x %02x)",
+			   (long) len, resp[0], resp[1]);
+		return -1;
+	}
+	get_resp[4] = resp[1];
+
+	len = sizeof(buf);
+	ret = scard_transmit(scard, get_resp, sizeof(get_resp), buf, &len);
+	if (ret != SCARD_S_SUCCESS || len > sizeof(buf))
+		return -1;
+
+	wpa_hexdump(MSG_DEBUG, "SCARD: UMTS get response result", buf, len);
+	if (len >= 2 + AKA_AUTS_LEN && buf[0] == 0xdc &&
+	    buf[1] == AKA_AUTS_LEN) {
+		wpa_printf(MSG_DEBUG, "SCARD: UMTS Synchronization-Failure");
+		os_memcpy(auts, buf + 2, AKA_AUTS_LEN);
+		wpa_hexdump(MSG_DEBUG, "SCARD: AUTS", auts, AKA_AUTS_LEN);
+		return -2;
+	} else if (len >= 6 + IK_LEN + CK_LEN && buf[0] == 0xdb) {
+		pos = buf + 1;
+		end = buf + len;
+
+		/* RES */
+		if (pos[0] > RES_MAX_LEN || pos[0] > end - pos) {
+			wpa_printf(MSG_DEBUG, "SCARD: Invalid RES");
+			return -1;
+		}
+		*res_len = *pos++;
+		os_memcpy(res, pos, *res_len);
+		pos += *res_len;
+		wpa_hexdump(MSG_DEBUG, "SCARD: RES", res, *res_len);
+
+		/* CK */
+		if (pos[0] != CK_LEN || CK_LEN > end - pos) {
+			wpa_printf(MSG_DEBUG, "SCARD: Invalid CK");
+			return -1;
+		}
+		pos++;
+		os_memcpy(ck, pos, CK_LEN);
+		pos += CK_LEN;
+		wpa_hexdump(MSG_DEBUG, "SCARD: CK", ck, CK_LEN);
+
+		/* IK */
+		if (pos[0] != IK_LEN || IK_LEN > end - pos) {
+			wpa_printf(MSG_DEBUG, "SCARD: Invalid IK");
+			return -1;
+		}
+		pos++;
+		os_memcpy(ik, pos, IK_LEN);
+		pos += IK_LEN;
+		wpa_hexdump(MSG_DEBUG, "SCARD: IK", ik, IK_LEN);
+
+		if (end > pos) {
+			wpa_hexdump(MSG_DEBUG,
+				    "SCARD: Ignore extra data in end",
+				    pos, end - pos);
+		}
+
+		return 0;
+	}
+
+	wpa_printf(MSG_DEBUG, "SCARD: Unrecognized response");
+	return -1;
+}
+
+
+int scard_supports_umts(struct scard_data *scard)
+{
+	return scard->sim_type == SCARD_USIM;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/pcsc_funcs.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/pcsc_funcs.h
new file mode 100644
index 0000000..eacd2a2
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/pcsc_funcs.h
@@ -0,0 +1,42 @@
+/*
+ * WPA Supplicant / PC/SC smartcard interface for USIM, GSM SIM
+ * Copyright (c) 2004-2006, 2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef PCSC_FUNCS_H
+#define PCSC_FUNCS_H
+
+#ifdef PCSC_FUNCS
+struct scard_data * scard_init(const char *reader);
+void scard_deinit(struct scard_data *scard);
+
+int scard_set_pin(struct scard_data *scard, const char *pin);
+int scard_get_imsi(struct scard_data *scard, char *imsi, size_t *len);
+int scard_get_mnc_len(struct scard_data *scard);
+int scard_gsm_auth(struct scard_data *scard, const unsigned char *_rand,
+		   unsigned char *sres, unsigned char *kc);
+int scard_umts_auth(struct scard_data *scard, const unsigned char *_rand,
+		    const unsigned char *autn,
+		    unsigned char *res, size_t *res_len,
+		    unsigned char *ik, unsigned char *ck, unsigned char *auts);
+int scard_get_pin_retry_counter(struct scard_data *scard);
+int scard_supports_umts(struct scard_data *scard);
+
+#else /* PCSC_FUNCS */
+
+#define scard_init(r) NULL
+#define scard_deinit(s) do { } while (0)
+#define scard_set_pin(s, p) -1
+#define scard_get_imsi(s, i, l) -1
+#define scard_get_mnc_len(s) -1
+#define scard_gsm_auth(s, r, s2, k) -1
+#define scard_umts_auth(s, r, a, r2, rl, i, c, a2) -1
+#define scard_get_pin_retry_counter(s) -1
+#define scard_supports_umts(s) 0
+
+#endif /* PCSC_FUNCS */
+
+#endif /* PCSC_FUNCS_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/platform.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/platform.h
new file mode 100644
index 0000000..b2ad856
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/platform.h
@@ -0,0 +1,18 @@
+/*
+ * Platform definitions for Radiotap parser
+ * Copyright (c) 2021, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef PLATFORM_H
+#define PLATFORM_H
+
+#include "includes.h"
+#include "common.h"
+
+#define get_unaligned_le16(p)	WPA_GET_LE16((void *) (p))
+#define get_unaligned_le32(p)	WPA_GET_LE32((void *) (p))
+
+#endif /* PLATFORM_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/radiotap.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/radiotap.c
new file mode 100644
index 0000000..6dfe298
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/radiotap.c
@@ -0,0 +1,396 @@
+/*
+ * Radiotap parser
+ *
+ * Copyright 2007		Andy Green <andy@warmcat.com>
+ * Copyright 2009		Johannes Berg <johannes@sipsolutions.net>
+ *
+ * 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.
+ *
+ * Alternatively, this software may be distributed under the terms of ISC
+ * license, see COPYING for more details.
+ */
+#include "platform.h"
+#include "radiotap_iter.h"
+
+/* function prototypes and related defs are in radiotap_iter.h */
+
+static const struct radiotap_align_size rtap_namespace_sizes[] = {
+	[IEEE80211_RADIOTAP_TSFT] = { .align = 8, .size = 8, },
+	[IEEE80211_RADIOTAP_FLAGS] = { .align = 1, .size = 1, },
+	[IEEE80211_RADIOTAP_RATE] = { .align = 1, .size = 1, },
+	[IEEE80211_RADIOTAP_CHANNEL] = { .align = 2, .size = 4, },
+	[IEEE80211_RADIOTAP_FHSS] = { .align = 2, .size = 2, },
+	[IEEE80211_RADIOTAP_DBM_ANTSIGNAL] = { .align = 1, .size = 1, },
+	[IEEE80211_RADIOTAP_DBM_ANTNOISE] = { .align = 1, .size = 1, },
+	[IEEE80211_RADIOTAP_LOCK_QUALITY] = { .align = 2, .size = 2, },
+	[IEEE80211_RADIOTAP_TX_ATTENUATION] = { .align = 2, .size = 2, },
+	[IEEE80211_RADIOTAP_DB_TX_ATTENUATION] = { .align = 2, .size = 2, },
+	[IEEE80211_RADIOTAP_DBM_TX_POWER] = { .align = 1, .size = 1, },
+	[IEEE80211_RADIOTAP_ANTENNA] = { .align = 1, .size = 1, },
+	[IEEE80211_RADIOTAP_DB_ANTSIGNAL] = { .align = 1, .size = 1, },
+	[IEEE80211_RADIOTAP_DB_ANTNOISE] = { .align = 1, .size = 1, },
+	[IEEE80211_RADIOTAP_RX_FLAGS] = { .align = 2, .size = 2, },
+	[IEEE80211_RADIOTAP_TX_FLAGS] = { .align = 2, .size = 2, },
+	[IEEE80211_RADIOTAP_RTS_RETRIES] = { .align = 1, .size = 1, },
+	[IEEE80211_RADIOTAP_DATA_RETRIES] = { .align = 1, .size = 1, },
+	[IEEE80211_RADIOTAP_MCS] = { .align = 1, .size = 3, },
+	[IEEE80211_RADIOTAP_AMPDU_STATUS] = { .align = 4, .size = 8, },
+	[IEEE80211_RADIOTAP_VHT] = { .align = 2, .size = 12, },
+	[IEEE80211_RADIOTAP_TIMESTAMP] = { .align = 8, .size = 12, },
+	/*
+	 * add more here as they are defined in radiotap.h
+	 */
+};
+
+static const struct ieee80211_radiotap_namespace radiotap_ns = {
+	.n_bits = sizeof(rtap_namespace_sizes) / sizeof(rtap_namespace_sizes[0]),
+	.align_size = rtap_namespace_sizes,
+};
+
+/**
+ * ieee80211_radiotap_iterator_init - radiotap parser iterator initialization
+ * @iterator: radiotap_iterator to initialize
+ * @radiotap_header: radiotap header to parse
+ * @max_length: total length we can parse into (eg, whole packet length)
+ *
+ * Returns: 0 or a negative error code if there is a problem.
+ *
+ * This function initializes an opaque iterator struct which can then
+ * be passed to ieee80211_radiotap_iterator_next() to visit every radiotap
+ * argument which is present in the header.  It knows about extended
+ * present headers and handles them.
+ *
+ * How to use:
+ * call __ieee80211_radiotap_iterator_init() to init a semi-opaque iterator
+ * struct ieee80211_radiotap_iterator (no need to init the struct beforehand)
+ * checking for a good 0 return code.  Then loop calling
+ * __ieee80211_radiotap_iterator_next()... it returns either 0,
+ * -ENOENT if there are no more args to parse, or -EINVAL if there is a problem.
+ * The iterator's @this_arg member points to the start of the argument
+ * associated with the current argument index that is present, which can be
+ * found in the iterator's @this_arg_index member.  This arg index corresponds
+ * to the IEEE80211_RADIOTAP_... defines.
+ *
+ * Radiotap header length:
+ * You can find the CPU-endian total radiotap header length in
+ * iterator->max_length after executing ieee80211_radiotap_iterator_init()
+ * successfully.
+ *
+ * Alignment Gotcha:
+ * You must take care when dereferencing iterator.this_arg
+ * for multibyte types... the pointer is not aligned.  Use
+ * get_unaligned((type *)iterator.this_arg) to dereference
+ * iterator.this_arg for type "type" safely on all arches.
+ *
+ * Example code: parse.c
+ */
+
+int ieee80211_radiotap_iterator_init(
+	struct ieee80211_radiotap_iterator *iterator,
+	struct ieee80211_radiotap_header *radiotap_header,
+	int max_length, const struct ieee80211_radiotap_vendor_namespaces *vns)
+{
+	/* must at least have the radiotap header */
+	if (max_length < (int)sizeof(struct ieee80211_radiotap_header))
+		return -EINVAL;
+
+	/* Linux only supports version 0 radiotap format */
+	if (radiotap_header->it_version)
+		return -EINVAL;
+
+	/* sanity check for allowed length and radiotap length field */
+	if (max_length < get_unaligned_le16(&radiotap_header->it_len))
+		return -EINVAL;
+
+	iterator->_rtheader = radiotap_header;
+	iterator->_max_length = get_unaligned_le16(&radiotap_header->it_len);
+	iterator->_arg_index = 0;
+	iterator->_bitmap_shifter = get_unaligned_le32(&radiotap_header->it_present);
+	iterator->_arg = (uint8_t *)radiotap_header + sizeof(*radiotap_header);
+	iterator->_next_ns_data = NULL;
+	iterator->_reset_on_ext = 0;
+	iterator->_next_bitmap = (le32 *) (((u8 *) radiotap_header) + offsetof(struct ieee80211_radiotap_header, it_present));
+	iterator->_next_bitmap++;
+	iterator->_vns = vns;
+	iterator->current_namespace = &radiotap_ns;
+	iterator->is_radiotap_ns = 1;
+#ifdef RADIOTAP_SUPPORT_OVERRIDES
+	iterator->n_overrides = 0;
+	iterator->overrides = NULL;
+#endif
+
+	/* find payload start allowing for extended bitmap(s) */
+
+	if (iterator->_bitmap_shifter & BIT(IEEE80211_RADIOTAP_EXT)) {
+		if ((unsigned long)iterator->_arg -
+		    (unsigned long)iterator->_rtheader + sizeof(uint32_t) >
+		    (unsigned long)iterator->_max_length)
+			return -EINVAL;
+		while (get_unaligned_le32(iterator->_arg) &
+		       BIT(IEEE80211_RADIOTAP_EXT)) {
+			iterator->_arg += sizeof(uint32_t);
+
+			/*
+			 * check for insanity where the present bitmaps
+			 * keep claiming to extend up to or even beyond the
+			 * stated radiotap header length
+			 */
+
+			if ((unsigned long)iterator->_arg -
+			    (unsigned long)iterator->_rtheader +
+			    sizeof(uint32_t) >
+			    (unsigned long)iterator->_max_length)
+				return -EINVAL;
+		}
+
+		iterator->_arg += sizeof(uint32_t);
+
+		/*
+		 * no need to check again for blowing past stated radiotap
+		 * header length, because ieee80211_radiotap_iterator_next
+		 * checks it before it is dereferenced
+		 */
+	}
+
+	iterator->this_arg = iterator->_arg;
+	iterator->this_arg_index = 0;
+	iterator->this_arg_size = 0;
+
+	/* we are all initialized happily */
+
+	return 0;
+}
+
+static void find_ns(struct ieee80211_radiotap_iterator *iterator,
+		    uint32_t oui, uint8_t subns)
+{
+	int i;
+
+	iterator->current_namespace = NULL;
+
+	if (!iterator->_vns)
+		return;
+
+	for (i = 0; i < iterator->_vns->n_ns; i++) {
+		if (iterator->_vns->ns[i].oui != oui)
+			continue;
+		if (iterator->_vns->ns[i].subns != subns)
+			continue;
+
+		iterator->current_namespace = &iterator->_vns->ns[i];
+		break;
+	}
+}
+
+#ifdef RADIOTAP_SUPPORT_OVERRIDES
+static int find_override(struct ieee80211_radiotap_iterator *iterator,
+			 int *align, int *size)
+{
+	int i;
+
+	if (!iterator->overrides)
+		return 0;
+
+	for (i = 0; i < iterator->n_overrides; i++) {
+		if (iterator->_arg_index == iterator->overrides[i].field) {
+			*align = iterator->overrides[i].align;
+			*size = iterator->overrides[i].size;
+			if (!*align) /* erroneous override */
+				return 0;
+			return 1;
+		}
+	}
+
+	return 0;
+}
+#endif
+
+
+/**
+ * ieee80211_radiotap_iterator_next - return next radiotap parser iterator arg
+ * @iterator: radiotap_iterator to move to next arg (if any)
+ *
+ * Returns: 0 if there is an argument to handle,
+ * -ENOENT if there are no more args or -EINVAL
+ * if there is something else wrong.
+ *
+ * This function provides the next radiotap arg index (IEEE80211_RADIOTAP_*)
+ * in @this_arg_index and sets @this_arg to point to the
+ * payload for the field.  It takes care of alignment handling and extended
+ * present fields.  @this_arg can be changed by the caller (eg,
+ * incremented to move inside a compound argument like
+ * IEEE80211_RADIOTAP_CHANNEL).  The args pointed to are in
+ * little-endian format whatever the endianness of your CPU.
+ *
+ * Alignment Gotcha:
+ * You must take care when dereferencing iterator.this_arg
+ * for multibyte types... the pointer is not aligned.  Use
+ * get_unaligned((type *)iterator.this_arg) to dereference
+ * iterator.this_arg for type "type" safely on all arches.
+ */
+
+int ieee80211_radiotap_iterator_next(
+	struct ieee80211_radiotap_iterator *iterator)
+{
+	while (1) {
+		int hit = 0;
+		int pad, align, size, subns;
+		uint32_t oui;
+
+		/* if no more EXT bits, that's it */
+		if ((iterator->_arg_index % 32) == IEEE80211_RADIOTAP_EXT &&
+		    !(iterator->_bitmap_shifter & 1))
+			return -ENOENT;
+
+		if (!(iterator->_bitmap_shifter & 1))
+			goto next_entry; /* arg not present */
+
+		/* get alignment/size of data */
+		switch (iterator->_arg_index % 32) {
+		case IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE:
+		case IEEE80211_RADIOTAP_EXT:
+			align = 1;
+			size = 0;
+			break;
+		case IEEE80211_RADIOTAP_VENDOR_NAMESPACE:
+			align = 2;
+			size = 6;
+			break;
+		default:
+#ifdef RADIOTAP_SUPPORT_OVERRIDES
+			if (find_override(iterator, &align, &size)) {
+				/* all set */
+			} else
+#endif
+			if (!iterator->current_namespace ||
+			    iterator->_arg_index >= iterator->current_namespace->n_bits) {
+				if (iterator->current_namespace == &radiotap_ns)
+					return -ENOENT;
+				align = 0;
+			} else {
+				align = iterator->current_namespace->align_size[iterator->_arg_index].align;
+				size = iterator->current_namespace->align_size[iterator->_arg_index].size;
+			}
+			if (!align) {
+				/* skip all subsequent data */
+				iterator->_arg = iterator->_next_ns_data;
+				/* give up on this namespace */
+				iterator->current_namespace = NULL;
+				goto next_entry;
+			}
+			break;
+		}
+
+		/*
+		 * arg is present, account for alignment padding
+		 *
+		 * Note that these alignments are relative to the start
+		 * of the radiotap header.  There is no guarantee
+		 * that the radiotap header itself is aligned on any
+		 * kind of boundary.
+		 *
+		 * The above is why get_unaligned() is used to dereference
+		 * multibyte elements from the radiotap area.
+		 */
+
+		pad = ((unsigned long)iterator->_arg -
+		       (unsigned long)iterator->_rtheader) & (align - 1);
+
+		if (pad)
+			iterator->_arg += align - pad;
+
+		if (iterator->_arg_index % 32 == IEEE80211_RADIOTAP_VENDOR_NAMESPACE) {
+			int vnslen;
+
+			if ((unsigned long)iterator->_arg + size -
+			    (unsigned long)iterator->_rtheader >
+			    (unsigned long)iterator->_max_length)
+				return -EINVAL;
+
+			oui = (*iterator->_arg << 16) |
+				(*(iterator->_arg + 1) << 8) |
+				*(iterator->_arg + 2);
+			subns = *(iterator->_arg + 3);
+
+			find_ns(iterator, oui, subns);
+
+			vnslen = get_unaligned_le16(iterator->_arg + 4);
+			iterator->_next_ns_data = iterator->_arg + size + vnslen;
+			if (!iterator->current_namespace)
+				size += vnslen;
+		}
+
+		/*
+		 * this is what we will return to user, but we need to
+		 * move on first so next call has something fresh to test
+		 */
+		iterator->this_arg_index = iterator->_arg_index;
+		iterator->this_arg = iterator->_arg;
+		iterator->this_arg_size = size;
+
+		/* internally move on the size of this arg */
+		iterator->_arg += size;
+
+		/*
+		 * check for insanity where we are given a bitmap that
+		 * claims to have more arg content than the length of the
+		 * radiotap section.  We will normally end up equalling this
+		 * max_length on the last arg, never exceeding it.
+		 */
+
+		if ((unsigned long)iterator->_arg -
+		    (unsigned long)iterator->_rtheader >
+		    (unsigned long)iterator->_max_length)
+			return -EINVAL;
+
+		/* these special ones are valid in each bitmap word */
+		switch (iterator->_arg_index % 32) {
+		case IEEE80211_RADIOTAP_VENDOR_NAMESPACE:
+			iterator->_reset_on_ext = 1;
+
+			iterator->is_radiotap_ns = 0;
+			/*
+			 * If parser didn't register this vendor
+			 * namespace with us, allow it to show it
+			 * as 'raw. Do do that, set argument index
+			 * to vendor namespace.
+			 */
+			iterator->this_arg_index =
+				IEEE80211_RADIOTAP_VENDOR_NAMESPACE;
+			if (!iterator->current_namespace)
+				hit = 1;
+			goto next_entry;
+		case IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE:
+			iterator->_reset_on_ext = 1;
+			iterator->current_namespace = &radiotap_ns;
+			iterator->is_radiotap_ns = 1;
+			goto next_entry;
+		case IEEE80211_RADIOTAP_EXT:
+			/*
+			 * bit 31 was set, there is more
+			 * -- move to next u32 bitmap
+			 */
+			iterator->_bitmap_shifter =
+				get_unaligned_le32(iterator->_next_bitmap);
+			iterator->_next_bitmap++;
+			if (iterator->_reset_on_ext)
+				iterator->_arg_index = 0;
+			else
+				iterator->_arg_index++;
+			iterator->_reset_on_ext = 0;
+			break;
+		default:
+			/* we've got a hit! */
+			hit = 1;
+ next_entry:
+			iterator->_bitmap_shifter >>= 1;
+			iterator->_arg_index++;
+		}
+
+		/* if we found a valid arg earlier, return it now */
+		if (hit)
+			return 0;
+	}
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/radiotap.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/radiotap.h
new file mode 100644
index 0000000..488d5a3
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/radiotap.h
@@ -0,0 +1,200 @@
+/*
+ * Copyright (c) 2017		Intel Deutschland GmbH
+ *
+ * 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 __RADIOTAP_H
+#define __RADIOTAP_H
+
+/**
+ * struct ieee82011_radiotap_header - base radiotap header
+ */
+struct ieee80211_radiotap_header {
+	/**
+	 * @it_version: radiotap version, always 0
+	 */
+	uint8_t it_version;
+
+	/**
+	 * @it_pad: padding (or alignment)
+	 */
+	uint8_t it_pad;
+
+	/**
+	 * @it_len: overall radiotap header length
+	 */
+	le16 it_len;
+
+	/**
+	 * @it_present: (first) present word
+	 */
+	le32 it_present;
+} STRUCT_PACKED;
+
+/* version is always 0 */
+#define PKTHDR_RADIOTAP_VERSION	0
+
+/* see the radiotap website for the descriptions */
+enum ieee80211_radiotap_presence {
+	IEEE80211_RADIOTAP_TSFT = 0,
+	IEEE80211_RADIOTAP_FLAGS = 1,
+	IEEE80211_RADIOTAP_RATE = 2,
+	IEEE80211_RADIOTAP_CHANNEL = 3,
+	IEEE80211_RADIOTAP_FHSS = 4,
+	IEEE80211_RADIOTAP_DBM_ANTSIGNAL = 5,
+	IEEE80211_RADIOTAP_DBM_ANTNOISE = 6,
+	IEEE80211_RADIOTAP_LOCK_QUALITY = 7,
+	IEEE80211_RADIOTAP_TX_ATTENUATION = 8,
+	IEEE80211_RADIOTAP_DB_TX_ATTENUATION = 9,
+	IEEE80211_RADIOTAP_DBM_TX_POWER = 10,
+	IEEE80211_RADIOTAP_ANTENNA = 11,
+	IEEE80211_RADIOTAP_DB_ANTSIGNAL = 12,
+	IEEE80211_RADIOTAP_DB_ANTNOISE = 13,
+	IEEE80211_RADIOTAP_RX_FLAGS = 14,
+	IEEE80211_RADIOTAP_TX_FLAGS = 15,
+	IEEE80211_RADIOTAP_RTS_RETRIES = 16,
+	IEEE80211_RADIOTAP_DATA_RETRIES = 17,
+	/* 18 is XChannel, but it's not defined yet */
+	IEEE80211_RADIOTAP_MCS = 19,
+	IEEE80211_RADIOTAP_AMPDU_STATUS = 20,
+	IEEE80211_RADIOTAP_VHT = 21,
+	IEEE80211_RADIOTAP_TIMESTAMP = 22,
+
+	/* valid in every it_present bitmap, even vendor namespaces */
+	IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE = 29,
+	IEEE80211_RADIOTAP_VENDOR_NAMESPACE = 30,
+	IEEE80211_RADIOTAP_EXT = 31
+};
+
+/* for IEEE80211_RADIOTAP_FLAGS */
+enum ieee80211_radiotap_flags {
+	IEEE80211_RADIOTAP_F_CFP = 0x01,
+	IEEE80211_RADIOTAP_F_SHORTPRE = 0x02,
+	IEEE80211_RADIOTAP_F_WEP = 0x04,
+	IEEE80211_RADIOTAP_F_FRAG = 0x08,
+	IEEE80211_RADIOTAP_F_FCS = 0x10,
+	IEEE80211_RADIOTAP_F_DATAPAD = 0x20,
+	IEEE80211_RADIOTAP_F_BADFCS = 0x40,
+};
+
+/* for IEEE80211_RADIOTAP_CHANNEL */
+enum ieee80211_radiotap_channel_flags {
+	IEEE80211_CHAN_CCK = 0x0020,
+	IEEE80211_CHAN_OFDM = 0x0040,
+	IEEE80211_CHAN_2GHZ = 0x0080,
+	IEEE80211_CHAN_5GHZ = 0x0100,
+	IEEE80211_CHAN_DYN = 0x0400,
+	IEEE80211_CHAN_HALF = 0x4000,
+	IEEE80211_CHAN_QUARTER = 0x8000,
+};
+
+/* for IEEE80211_RADIOTAP_RX_FLAGS */
+enum ieee80211_radiotap_rx_flags {
+	IEEE80211_RADIOTAP_F_RX_BADPLCP = 0x0002,
+};
+
+/* for IEEE80211_RADIOTAP_TX_FLAGS */
+enum ieee80211_radiotap_tx_flags {
+	IEEE80211_RADIOTAP_F_TX_FAIL = 0x0001,
+	IEEE80211_RADIOTAP_F_TX_CTS = 0x0002,
+	IEEE80211_RADIOTAP_F_TX_RTS = 0x0004,
+	IEEE80211_RADIOTAP_F_TX_NOACK = 0x0008,
+};
+
+/* for IEEE80211_RADIOTAP_MCS "have" flags */
+enum ieee80211_radiotap_mcs_have {
+	IEEE80211_RADIOTAP_MCS_HAVE_BW = 0x01,
+	IEEE80211_RADIOTAP_MCS_HAVE_MCS = 0x02,
+	IEEE80211_RADIOTAP_MCS_HAVE_GI = 0x04,
+	IEEE80211_RADIOTAP_MCS_HAVE_FMT = 0x08,
+	IEEE80211_RADIOTAP_MCS_HAVE_FEC = 0x10,
+	IEEE80211_RADIOTAP_MCS_HAVE_STBC = 0x20,
+};
+
+enum ieee80211_radiotap_mcs_flags {
+	IEEE80211_RADIOTAP_MCS_BW_MASK = 0x03,
+	IEEE80211_RADIOTAP_MCS_BW_20 = 0,
+	IEEE80211_RADIOTAP_MCS_BW_40 = 1,
+	IEEE80211_RADIOTAP_MCS_BW_20L = 2,
+	IEEE80211_RADIOTAP_MCS_BW_20U = 3,
+
+	IEEE80211_RADIOTAP_MCS_SGI = 0x04,
+	IEEE80211_RADIOTAP_MCS_FMT_GF = 0x08,
+	IEEE80211_RADIOTAP_MCS_FEC_LDPC = 0x10,
+	IEEE80211_RADIOTAP_MCS_STBC_MASK = 0x60,
+	IEEE80211_RADIOTAP_MCS_STBC_1 = 1,
+	IEEE80211_RADIOTAP_MCS_STBC_2 = 2,
+	IEEE80211_RADIOTAP_MCS_STBC_3 = 3,
+	IEEE80211_RADIOTAP_MCS_STBC_SHIFT = 5,
+};
+
+/* for IEEE80211_RADIOTAP_AMPDU_STATUS */
+enum ieee80211_radiotap_ampdu_flags {
+	IEEE80211_RADIOTAP_AMPDU_REPORT_ZEROLEN = 0x0001,
+	IEEE80211_RADIOTAP_AMPDU_IS_ZEROLEN = 0x0002,
+	IEEE80211_RADIOTAP_AMPDU_LAST_KNOWN = 0x0004,
+	IEEE80211_RADIOTAP_AMPDU_IS_LAST = 0x0008,
+	IEEE80211_RADIOTAP_AMPDU_DELIM_CRC_ERR = 0x0010,
+	IEEE80211_RADIOTAP_AMPDU_DELIM_CRC_KNOWN = 0x0020,
+};
+
+/* for IEEE80211_RADIOTAP_VHT */
+enum ieee80211_radiotap_vht_known {
+	IEEE80211_RADIOTAP_VHT_KNOWN_STBC = 0x0001,
+	IEEE80211_RADIOTAP_VHT_KNOWN_TXOP_PS_NA = 0x0002,
+	IEEE80211_RADIOTAP_VHT_KNOWN_GI = 0x0004,
+	IEEE80211_RADIOTAP_VHT_KNOWN_SGI_NSYM_DIS = 0x0008,
+	IEEE80211_RADIOTAP_VHT_KNOWN_LDPC_EXTRA_OFDM_SYM = 0x0010,
+	IEEE80211_RADIOTAP_VHT_KNOWN_BEAMFORMED = 0x0020,
+	IEEE80211_RADIOTAP_VHT_KNOWN_BANDWIDTH = 0x0040,
+	IEEE80211_RADIOTAP_VHT_KNOWN_GROUP_ID = 0x0080,
+	IEEE80211_RADIOTAP_VHT_KNOWN_PARTIAL_AID = 0x0100,
+};
+
+enum ieee80211_radiotap_vht_flags {
+	IEEE80211_RADIOTAP_VHT_FLAG_STBC = 0x01,
+	IEEE80211_RADIOTAP_VHT_FLAG_TXOP_PS_NA = 0x02,
+	IEEE80211_RADIOTAP_VHT_FLAG_SGI = 0x04,
+	IEEE80211_RADIOTAP_VHT_FLAG_SGI_NSYM_M10_9 = 0x08,
+	IEEE80211_RADIOTAP_VHT_FLAG_LDPC_EXTRA_OFDM_SYM = 0x10,
+	IEEE80211_RADIOTAP_VHT_FLAG_BEAMFORMED = 0x20,
+};
+
+enum ieee80211_radiotap_vht_coding {
+	IEEE80211_RADIOTAP_CODING_LDPC_USER0 = 0x01,
+	IEEE80211_RADIOTAP_CODING_LDPC_USER1 = 0x02,
+	IEEE80211_RADIOTAP_CODING_LDPC_USER2 = 0x04,
+	IEEE80211_RADIOTAP_CODING_LDPC_USER3 = 0x08,
+};
+
+/* for IEEE80211_RADIOTAP_TIMESTAMP */
+enum ieee80211_radiotap_timestamp_unit_spos {
+	IEEE80211_RADIOTAP_TIMESTAMP_UNIT_MASK = 0x000F,
+	IEEE80211_RADIOTAP_TIMESTAMP_UNIT_MS = 0x0000,
+	IEEE80211_RADIOTAP_TIMESTAMP_UNIT_US = 0x0001,
+	IEEE80211_RADIOTAP_TIMESTAMP_UNIT_NS = 0x0003,
+	IEEE80211_RADIOTAP_TIMESTAMP_SPOS_MASK = 0x00F0,
+	IEEE80211_RADIOTAP_TIMESTAMP_SPOS_BEGIN_MDPU = 0x0000,
+	IEEE80211_RADIOTAP_TIMESTAMP_SPOS_PLCP_SIG_ACQ = 0x0010,
+	IEEE80211_RADIOTAP_TIMESTAMP_SPOS_EO_PPDU = 0x0020,
+	IEEE80211_RADIOTAP_TIMESTAMP_SPOS_EO_MPDU = 0x0030,
+	IEEE80211_RADIOTAP_TIMESTAMP_SPOS_UNKNOWN = 0x00F0,
+};
+
+enum ieee80211_radiotap_timestamp_flags {
+	IEEE80211_RADIOTAP_TIMESTAMP_FLAG_64BIT = 0x00,
+	IEEE80211_RADIOTAP_TIMESTAMP_FLAG_32BIT = 0x01,
+	IEEE80211_RADIOTAP_TIMESTAMP_FLAG_ACCURACY = 0x02,
+};
+
+#endif /* __RADIOTAP_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/radiotap_iter.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/radiotap_iter.h
new file mode 100644
index 0000000..6ea07e3
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/radiotap_iter.h
@@ -0,0 +1,96 @@
+#ifndef __RADIOTAP_ITER_H
+#define __RADIOTAP_ITER_H
+
+#include <stdint.h>
+#include "radiotap.h"
+
+/* Radiotap header iteration
+ *   implemented in radiotap.c
+ */
+
+struct radiotap_override {
+	uint8_t field;
+	uint8_t align:4, size:4;
+};
+
+struct radiotap_align_size {
+	uint8_t align:4, size:4;
+};
+
+struct ieee80211_radiotap_namespace {
+	const struct radiotap_align_size *align_size;
+	int n_bits;
+	uint32_t oui;
+	uint8_t subns;
+};
+
+struct ieee80211_radiotap_vendor_namespaces {
+	const struct ieee80211_radiotap_namespace *ns;
+	int n_ns;
+};
+
+/**
+ * struct ieee80211_radiotap_iterator - tracks walk thru present radiotap args
+ * @this_arg_index: index of current arg, valid after each successful call
+ *	to ieee80211_radiotap_iterator_next()
+ * @this_arg: pointer to current radiotap arg; it is valid after each
+ *	call to ieee80211_radiotap_iterator_next() but also after
+ *	ieee80211_radiotap_iterator_init() where it will point to
+ *	the beginning of the actual data portion
+ * @this_arg_size: length of the current arg, for convenience
+ * @current_namespace: pointer to the current namespace definition
+ *	(or internally %NULL if the current namespace is unknown)
+ * @is_radiotap_ns: indicates whether the current namespace is the default
+ *	radiotap namespace or not
+ *
+ * @overrides: override standard radiotap fields
+ * @n_overrides: number of overrides
+ *
+ * @_rtheader: pointer to the radiotap header we are walking through
+ * @_max_length: length of radiotap header in cpu byte ordering
+ * @_arg_index: next argument index
+ * @_arg: next argument pointer
+ * @_next_bitmap: internal pointer to next present u32
+ * @_bitmap_shifter: internal shifter for curr u32 bitmap, b0 set == arg present
+ * @_vns: vendor namespace definitions
+ * @_next_ns_data: beginning of the next namespace's data
+ * @_reset_on_ext: internal; reset the arg index to 0 when going to the
+ *	next bitmap word
+ *
+ * Describes the radiotap parser state. Fields prefixed with an underscore
+ * must not be used by users of the parser, only by the parser internally.
+ */
+
+struct ieee80211_radiotap_iterator {
+	struct ieee80211_radiotap_header *_rtheader;
+	const struct ieee80211_radiotap_vendor_namespaces *_vns;
+	const struct ieee80211_radiotap_namespace *current_namespace;
+
+	unsigned char *_arg, *_next_ns_data;
+	le32 *_next_bitmap;
+
+	unsigned char *this_arg;
+#ifdef RADIOTAP_SUPPORT_OVERRIDES
+	const struct radiotap_override *overrides;
+	int n_overrides;
+#endif
+	int this_arg_index;
+	int this_arg_size;
+
+	int is_radiotap_ns;
+
+	int _max_length;
+	int _arg_index;
+	uint32_t _bitmap_shifter;
+	int _reset_on_ext;
+};
+
+extern int ieee80211_radiotap_iterator_init(
+	struct ieee80211_radiotap_iterator *iterator,
+	struct ieee80211_radiotap_header *radiotap_header,
+	int max_length, const struct ieee80211_radiotap_vendor_namespaces *vns);
+
+extern int ieee80211_radiotap_iterator_next(
+	struct ieee80211_radiotap_iterator *iterator);
+
+#endif /* __RADIOTAP_ITER_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/state_machine.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/state_machine.h
new file mode 100644
index 0000000..204c8a8
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/state_machine.h
@@ -0,0 +1,138 @@
+/*
+ * wpa_supplicant/hostapd - State machine definitions
+ * Copyright (c) 2002-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ *
+ * This file includes a set of pre-processor macros that can be used to
+ * implement a state machine. In addition to including this header file, each
+ * file implementing a state machine must define STATE_MACHINE_DATA to be the
+ * data structure including state variables (enum machine_state,
+ * bool changed), and STATE_MACHINE_DEBUG_PREFIX to be a string that is used
+ * as a prefix for all debug messages. If SM_ENTRY_MA macro is used to define
+ * a group of state machines with shared data structure, STATE_MACHINE_ADDR
+ * needs to be defined to point to the MAC address used in debug output.
+ * SM_ENTRY_M macro can be used to define similar group of state machines
+ * without this additional debug info.
+ */
+
+#ifndef STATE_MACHINE_H
+#define STATE_MACHINE_H
+
+/**
+ * SM_STATE - Declaration of a state machine function
+ * @machine: State machine name
+ * @state: State machine state
+ *
+ * This macro is used to declare a state machine function. It is used in place
+ * of a C function definition to declare functions to be run when the state is
+ * entered by calling SM_ENTER or SM_ENTER_GLOBAL.
+ */
+#define SM_STATE(machine, state) \
+static void sm_ ## machine ## _ ## state ## _Enter(STATE_MACHINE_DATA *sm, \
+	int global)
+
+/**
+ * SM_ENTRY - State machine function entry point
+ * @machine: State machine name
+ * @state: State machine state
+ *
+ * This macro is used inside each state machine function declared with
+ * SM_STATE. SM_ENTRY should be in the beginning of the function body, but
+ * after declaration of possible local variables. This macro prints debug
+ * information about state transition and update the state machine state.
+ */
+#define SM_ENTRY(machine, state) \
+if (!global || sm->machine ## _state != machine ## _ ## state) { \
+	sm->changed = true; \
+	wpa_printf(MSG_DEBUG, STATE_MACHINE_DEBUG_PREFIX ": " #machine \
+		   " entering state " #state); \
+} \
+sm->machine ## _state = machine ## _ ## state;
+
+/**
+ * SM_ENTRY_M - State machine function entry point for state machine group
+ * @machine: State machine name
+ * @_state: State machine state
+ * @data: State variable prefix (full variable: prefix_state)
+ *
+ * This macro is like SM_ENTRY, but for state machine groups that use a shared
+ * data structure for more than one state machine. Both machine and prefix
+ * parameters are set to "sub-state machine" name. prefix is used to allow more
+ * than one state variable to be stored in the same data structure.
+ */
+#define SM_ENTRY_M(machine, _state, data) \
+if (!global || sm->data ## _ ## state != machine ## _ ## _state) { \
+	sm->changed = true; \
+	wpa_printf(MSG_DEBUG, STATE_MACHINE_DEBUG_PREFIX ": " \
+		   #machine " entering state " #_state); \
+} \
+sm->data ## _ ## state = machine ## _ ## _state;
+
+/**
+ * SM_ENTRY_MA - State machine function entry point for state machine group
+ * @machine: State machine name
+ * @_state: State machine state
+ * @data: State variable prefix (full variable: prefix_state)
+ *
+ * This macro is like SM_ENTRY_M, but a MAC address is included in debug
+ * output. STATE_MACHINE_ADDR has to be defined to point to the MAC address to
+ * be included in debug.
+ */
+#define SM_ENTRY_MA(machine, _state, data) \
+if (!global || sm->data ## _ ## state != machine ## _ ## _state) { \
+	sm->changed = true; \
+	wpa_printf(MSG_DEBUG, STATE_MACHINE_DEBUG_PREFIX ": " MACSTR " " \
+		   #machine " entering state " #_state, \
+		   MAC2STR(STATE_MACHINE_ADDR)); \
+} \
+sm->data ## _ ## state = machine ## _ ## _state;
+
+/**
+ * SM_ENTER - Enter a new state machine state
+ * @machine: State machine name
+ * @state: State machine state
+ *
+ * This macro expands to a function call to a state machine function defined
+ * with SM_STATE macro. SM_ENTER is used in a state machine step function to
+ * move the state machine to a new state.
+ */
+#define SM_ENTER(machine, state) \
+sm_ ## machine ## _ ## state ## _Enter(sm, 0)
+
+/**
+ * SM_ENTER_GLOBAL - Enter a new state machine state based on global rule
+ * @machine: State machine name
+ * @state: State machine state
+ *
+ * This macro is like SM_ENTER, but this is used when entering a new state
+ * based on a global (not specific to any particular state) rule. A separate
+ * macro is used to avoid unwanted debug message floods when the same global
+ * rule is forcing a state machine to remain in on state.
+ */
+#define SM_ENTER_GLOBAL(machine, state) \
+sm_ ## machine ## _ ## state ## _Enter(sm, 1)
+
+/**
+ * SM_STEP - Declaration of a state machine step function
+ * @machine: State machine name
+ *
+ * This macro is used to declare a state machine step function. It is used in
+ * place of a C function definition to declare a function that is used to move
+ * state machine to a new state based on state variables. This function uses
+ * SM_ENTER and SM_ENTER_GLOBAL macros to enter new state.
+ */
+#define SM_STEP(machine) \
+static void sm_ ## machine ## _Step(STATE_MACHINE_DATA *sm)
+
+/**
+ * SM_STEP_RUN - Call the state machine step function
+ * @machine: State machine name
+ *
+ * This macro expands to a function call to a state machine step function
+ * defined with SM_STEP macro.
+ */
+#define SM_STEP_RUN(machine) sm_ ## machine ## _Step(sm)
+
+#endif /* STATE_MACHINE_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/trace.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/trace.c
new file mode 100644
index 0000000..8f12da8
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/trace.c
@@ -0,0 +1,420 @@
+/*
+ * Backtrace debugging
+ * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifdef WPA_TRACE_BFD
+#define _GNU_SOURCE
+#include <link.h>
+#endif /* WPA_TRACE_BCD */
+#include "includes.h"
+
+#include "common.h"
+#include "trace.h"
+
+#ifdef WPA_TRACE
+
+static struct dl_list active_references =
+{ &active_references, &active_references };
+
+#ifdef WPA_TRACE_BFD
+#include <bfd.h>
+
+#define DMGL_PARAMS      (1 << 0)
+#define DMGL_ANSI        (1 << 1)
+
+static char *prg_fname = NULL;
+static bfd *cached_abfd = NULL;
+static asymbol **syms = NULL;
+static unsigned long start_offset;
+static int start_offset_looked_up;
+
+
+static int callback(struct dl_phdr_info *info, size_t size, void *data)
+{
+	/*
+	 * dl_iterate_phdr(3):
+	 * "The first object visited by callback is the main program."
+	 */
+	start_offset = info->dlpi_addr;
+
+	/*
+	 * dl_iterate_phdr(3):
+	 * "The dl_iterate_phdr() function walks through the list of an
+	 *  application's shared objects and calls the function callback
+	 *  once for each object, until either all shared objects have
+	 *  been processed or callback returns a nonzero value."
+	 */
+	return 1;
+}
+
+
+static void get_prg_fname(void)
+{
+	char exe[50], fname[512];
+	int len;
+	os_snprintf(exe, sizeof(exe) - 1, "/proc/%u/exe", getpid());
+	len = readlink(exe, fname, sizeof(fname) - 1);
+	if (len < 0 || len >= (int) sizeof(fname)) {
+		wpa_printf(MSG_ERROR, "readlink: %s", strerror(errno));
+		return;
+	}
+	fname[len] = '\0';
+	prg_fname = strdup(fname);
+}
+
+
+static bfd * open_bfd(const char *fname)
+{
+	bfd *abfd;
+	char **matching;
+
+	abfd = bfd_openr(prg_fname, NULL);
+	if (abfd == NULL) {
+		wpa_printf(MSG_INFO, "bfd_openr failed");
+		return NULL;
+	}
+
+	if (bfd_check_format(abfd, bfd_archive)) {
+		wpa_printf(MSG_INFO, "bfd_check_format failed");
+		bfd_close(abfd);
+		return NULL;
+	}
+
+	if (!bfd_check_format_matches(abfd, bfd_object, &matching)) {
+		wpa_printf(MSG_INFO, "bfd_check_format_matches failed");
+		free(matching);
+		bfd_close(abfd);
+		return NULL;
+	}
+
+	return abfd;
+}
+
+
+static void read_syms(bfd *abfd)
+{
+	long storage, symcount;
+	bfd_boolean dynamic = FALSE;
+
+	if (syms)
+		return;
+
+	if (!(bfd_get_file_flags(abfd) & HAS_SYMS)) {
+		wpa_printf(MSG_INFO, "No symbols");
+		return;
+	}
+
+	storage = bfd_get_symtab_upper_bound(abfd);
+	if (storage == 0) {
+		storage = bfd_get_dynamic_symtab_upper_bound(abfd);
+		dynamic = TRUE;
+	}
+	if (storage < 0) {
+		wpa_printf(MSG_INFO, "Unknown symtab upper bound");
+		return;
+	}
+
+	syms = malloc(storage);
+	if (syms == NULL) {
+		wpa_printf(MSG_INFO, "Failed to allocate memory for symtab "
+			   "(%ld bytes)", storage);
+		return;
+	}
+	if (dynamic)
+		symcount = bfd_canonicalize_dynamic_symtab(abfd, syms);
+	else
+		symcount = bfd_canonicalize_symtab(abfd, syms);
+	if (symcount < 0) {
+		wpa_printf(MSG_INFO, "Failed to canonicalize %ssymtab",
+			   dynamic ? "dynamic " : "");
+		free(syms);
+		syms = NULL;
+		return;
+	}
+}
+
+
+struct bfd_data {
+	bfd_vma pc;
+	bfd_boolean found;
+	const char *filename;
+	const char *function;
+	unsigned int line;
+};
+
+/*
+ * binutils removed the bfd parameter and renamed things but
+ * those were macros so we can detect their absence.
+ * Cf. https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;a=commitdiff;h=fd3619828e94a24a92cddec42cbc0ab33352eeb4;hp=5dfda3562a69686c43aad4fb0269cc9d5ec010d5
+ */
+#ifndef bfd_get_section_vma
+#define bfd_get_section_vma(bfd, section) bfd_section_vma(section)
+#endif
+#ifndef bfd_get_section_size
+#define bfd_get_section_size bfd_section_size
+#endif
+
+static void find_addr_sect(bfd *abfd, asection *section, void *obj)
+{
+	struct bfd_data *data = obj;
+	bfd_vma vma;
+	bfd_size_type size;
+
+	if (data->found)
+		return;
+
+	if (!(bfd_get_section_vma(abfd, section)))
+		return;
+
+	vma = bfd_get_section_vma(abfd, section);
+	if (data->pc < vma)
+		return;
+
+	size = bfd_get_section_size(section);
+	if (data->pc >= vma + size)
+		return;
+
+	data->found = bfd_find_nearest_line(abfd, section, syms,
+					    data->pc - vma,
+					    &data->filename,
+					    &data->function,
+					    &data->line);
+}
+
+
+static void wpa_trace_bfd_addr(void *pc)
+{
+	bfd *abfd = cached_abfd;
+	struct bfd_data data;
+	const char *name;
+	char *aname = NULL;
+	const char *filename;
+
+	if (abfd == NULL)
+		return;
+
+	data.pc = (bfd_hostptr_t) ((u8 *) pc - start_offset);
+	data.found = FALSE;
+	bfd_map_over_sections(abfd, find_addr_sect, &data);
+
+	if (!data.found)
+		return;
+
+	do {
+		if (data.function)
+			aname = bfd_demangle(abfd, data.function,
+					     DMGL_ANSI | DMGL_PARAMS);
+		name = aname ? aname : data.function;
+		filename = data.filename;
+		if (filename) {
+			char *end = os_strrchr(filename, '/');
+			int i = 0;
+			while (*filename && *filename == prg_fname[i] &&
+			       filename <= end) {
+				filename++;
+				i++;
+			}
+		}
+		wpa_printf(MSG_INFO, "     %s() %s:%u",
+			   name, filename, data.line);
+		free(aname);
+		aname = NULL;
+
+		data.found = bfd_find_inliner_info(abfd, &data.filename,
+						   &data.function, &data.line);
+	} while (data.found);
+}
+
+
+static const char * wpa_trace_bfd_addr2func(void *pc)
+{
+	bfd *abfd = cached_abfd;
+	struct bfd_data data;
+
+	if (abfd == NULL)
+		return NULL;
+
+	data.pc = (bfd_hostptr_t) ((u8 *) pc - start_offset);
+	data.found = FALSE;
+	bfd_map_over_sections(abfd, find_addr_sect, &data);
+
+	if (!data.found)
+		return NULL;
+
+	return data.function;
+}
+
+
+static void wpa_trace_bfd_init(void)
+{
+	if (!prg_fname) {
+		get_prg_fname();
+		if (!prg_fname)
+			return;
+	}
+
+	if (!cached_abfd) {
+		cached_abfd = open_bfd(prg_fname);
+		if (!cached_abfd) {
+			wpa_printf(MSG_INFO, "Failed to open bfd");
+			return;
+		}
+	}
+
+	read_syms(cached_abfd);
+	if (!syms) {
+		wpa_printf(MSG_INFO, "Failed to read symbols");
+		return;
+	}
+
+	if (!start_offset_looked_up) {
+		dl_iterate_phdr(callback, NULL);
+		start_offset_looked_up = 1;
+	}
+}
+
+
+void wpa_trace_dump_funcname(const char *title, void *pc)
+{
+	wpa_printf(MSG_INFO, "WPA_TRACE: %s: %p", title, pc);
+	wpa_trace_bfd_init();
+	wpa_trace_bfd_addr(pc);
+}
+
+
+size_t wpa_trace_calling_func(const char *buf[], size_t len)
+{
+	bfd *abfd;
+	void *btrace_res[WPA_TRACE_LEN];
+	int i, btrace_num;
+	size_t pos = 0;
+
+	if (len == 0)
+		return 0;
+	if (len > WPA_TRACE_LEN)
+		len = WPA_TRACE_LEN;
+
+	wpa_trace_bfd_init();
+	abfd = cached_abfd;
+	if (!abfd)
+		return 0;
+
+	btrace_num = backtrace(btrace_res, len);
+	if (btrace_num < 1)
+		return 0;
+
+	for (i = 0; i < btrace_num; i++) {
+		struct bfd_data data;
+
+		data.pc = (bfd_hostptr_t) ((u8 *) btrace_res[i] - start_offset);
+		data.found = FALSE;
+		bfd_map_over_sections(abfd, find_addr_sect, &data);
+
+		while (data.found) {
+			if (data.function &&
+			    (pos > 0 ||
+			     os_strcmp(data.function, __func__) != 0)) {
+				buf[pos++] = data.function;
+				if (pos == len)
+					return pos;
+			}
+
+			data.found = bfd_find_inliner_info(abfd, &data.filename,
+							   &data.function,
+							   &data.line);
+		}
+	}
+
+	return pos;
+}
+
+#else /* WPA_TRACE_BFD */
+
+#define wpa_trace_bfd_init() do { } while (0)
+#define wpa_trace_bfd_addr(pc) do { } while (0)
+#define wpa_trace_bfd_addr2func(pc) NULL
+
+#endif /* WPA_TRACE_BFD */
+
+void wpa_trace_dump_func(const char *title, void **btrace, int btrace_num)
+{
+	char **sym;
+	int i;
+	enum { TRACE_HEAD, TRACE_RELEVANT, TRACE_TAIL } state;
+
+	wpa_trace_bfd_init();
+	wpa_printf(MSG_INFO, "WPA_TRACE: %s - START", title);
+	sym = backtrace_symbols(btrace, btrace_num);
+	state = TRACE_HEAD;
+	for (i = 0; i < btrace_num; i++) {
+		const char *func = wpa_trace_bfd_addr2func(btrace[i]);
+		if (state == TRACE_HEAD && func &&
+		    (os_strcmp(func, "wpa_trace_add_ref_func") == 0 ||
+		     os_strcmp(func, "wpa_trace_check_ref") == 0 ||
+		     os_strcmp(func, "wpa_trace_show") == 0))
+			continue;
+		if (state == TRACE_TAIL && sym && sym[i] &&
+		    os_strstr(sym[i], "__libc_start_main"))
+			break;
+		if (state == TRACE_HEAD)
+			state = TRACE_RELEVANT;
+		if (sym)
+			wpa_printf(MSG_INFO, "[%d]: %s", i, sym[i]);
+		else
+			wpa_printf(MSG_INFO, "[%d]: ?? [%p]", i, btrace[i]);
+		wpa_trace_bfd_addr(btrace[i]);
+		if (state == TRACE_RELEVANT && func &&
+		    os_strcmp(func, "main") == 0)
+			state = TRACE_TAIL;
+	}
+	free(sym);
+	wpa_printf(MSG_INFO, "WPA_TRACE: %s - END", title);
+}
+
+
+void wpa_trace_show(const char *title)
+{
+	struct info {
+		WPA_TRACE_INFO
+	} info;
+	wpa_trace_record(&info);
+	wpa_trace_dump(title, &info);
+}
+
+
+void wpa_trace_add_ref_func(struct wpa_trace_ref *ref, const void *addr)
+{
+	if (addr == NULL)
+		return;
+	ref->addr = addr;
+	wpa_trace_record(ref);
+	dl_list_add(&active_references, &ref->list);
+}
+
+
+void wpa_trace_check_ref(const void *addr)
+{
+	struct wpa_trace_ref *ref;
+	dl_list_for_each(ref, &active_references, struct wpa_trace_ref, list) {
+		if (addr != ref->addr)
+			continue;
+		wpa_trace_show("Freeing referenced memory");
+		wpa_trace_dump("Reference registration", ref);
+		abort();
+	}
+}
+
+
+void wpa_trace_deinit(void)
+{
+#ifdef WPA_TRACE_BFD
+	free(syms);
+	syms = NULL;
+#endif /* WPA_TRACE_BFD */
+}
+
+#endif /* WPA_TRACE */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/trace.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/trace.h
new file mode 100644
index 0000000..d1636de
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/trace.h
@@ -0,0 +1,71 @@
+/*
+ * Backtrace debugging
+ * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef TRACE_H
+#define TRACE_H
+
+#define WPA_TRACE_LEN 16
+
+#ifdef WPA_TRACE
+#include <execinfo.h>
+
+#include "list.h"
+
+#define WPA_TRACE_INFO void *btrace[WPA_TRACE_LEN]; int btrace_num;
+
+struct wpa_trace_ref {
+	struct dl_list list;
+	const void *addr;
+	WPA_TRACE_INFO
+};
+#define WPA_TRACE_REF(name) struct wpa_trace_ref wpa_trace_ref_##name
+
+#define wpa_trace_dump(title, ptr) \
+	wpa_trace_dump_func((title), (ptr)->btrace, (ptr)->btrace_num)
+void wpa_trace_dump_func(const char *title, void **btrace, int btrace_num);
+#define wpa_trace_record(ptr) \
+	(ptr)->btrace_num = backtrace((ptr)->btrace, WPA_TRACE_LEN)
+void wpa_trace_show(const char *title);
+#define wpa_trace_add_ref(ptr, name, addr) \
+	wpa_trace_add_ref_func(&(ptr)->wpa_trace_ref_##name, (addr))
+void wpa_trace_add_ref_func(struct wpa_trace_ref *ref, const void *addr);
+#define wpa_trace_remove_ref(ptr, name, addr)	\
+	do { \
+		if ((addr)) \
+			dl_list_del(&(ptr)->wpa_trace_ref_##name.list); \
+	} while (0)
+void wpa_trace_check_ref(const void *addr);
+size_t wpa_trace_calling_func(const char *buf[], size_t len);
+
+#else /* WPA_TRACE */
+
+#define WPA_TRACE_INFO
+#define WPA_TRACE_REF(n)
+#define wpa_trace_dump(title, ptr) do { } while (0)
+#define wpa_trace_record(ptr) do { } while (0)
+#define wpa_trace_show(title) do { } while (0)
+#define wpa_trace_add_ref(ptr, name, addr) do { } while (0)
+#define wpa_trace_remove_ref(ptr, name, addr) do { } while (0)
+#define wpa_trace_check_ref(addr) do { } while (0)
+
+#endif /* WPA_TRACE */
+
+
+#ifdef WPA_TRACE_BFD
+
+void wpa_trace_dump_funcname(const char *title, void *pc);
+
+#else /* WPA_TRACE_BFD */
+
+#define wpa_trace_dump_funcname(title, pc) do { } while (0)
+
+#endif /* WPA_TRACE_BFD */
+
+void wpa_trace_deinit(void);
+
+#endif /* TRACE_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/utils_module_tests.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/utils_module_tests.c
new file mode 100644
index 0000000..365f21f
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/utils_module_tests.c
@@ -0,0 +1,1234 @@
+/*
+ * utils module tests
+ * Copyright (c) 2014-2015, Jouni Malinen <j@w1.fi>
+ *
+ * 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/const_time.h"
+#include "common/ieee802_11_defs.h"
+#include "utils/bitfield.h"
+#include "utils/ext_password.h"
+#include "utils/trace.h"
+#include "utils/base64.h"
+#include "utils/ip_addr.h"
+#include "utils/eloop.h"
+#include "utils/json.h"
+#include "utils/module_tests.h"
+
+
+struct printf_test_data {
+	u8 *data;
+	size_t len;
+	char *encoded;
+};
+
+static const struct printf_test_data printf_tests[] = {
+	{ (u8 *) "abcde", 5, "abcde" },
+	{ (u8 *) "a\0b\nc\ed\re\tf\"\\", 13, "a\\0b\\nc\\ed\\re\\tf\\\"\\\\" },
+	{ (u8 *) "\x00\x31\x00\x32\x00\x39", 6, "\\x001\\0002\\09" },
+	{ (u8 *) "\n\n\n", 3, "\n\12\x0a" },
+	{ (u8 *) "\303\245\303\244\303\266\303\205\303\204\303\226", 12,
+	  "\\xc3\\xa5\xc3\\xa4\\xc3\\xb6\\xc3\\x85\\xc3\\x84\\xc3\\x96" },
+	{ (u8 *) "\303\245\303\244\303\266\303\205\303\204\303\226", 12,
+	  "\\303\\245\\303\\244\\303\\266\\303\\205\\303\\204\\303\\226" },
+	{ (u8 *) "\xe5\xe4\xf6\xc5\xc4\xd6", 6,
+	  "\\xe5\\xe4\\xf6\\xc5\\xc4\\xd6" },
+	{ NULL, 0, NULL }
+};
+
+
+static int printf_encode_decode_tests(void)
+{
+	int i;
+	size_t binlen;
+	char buf[100];
+	u8 bin[100];
+	int errors = 0;
+	int array[10];
+
+	wpa_printf(MSG_INFO, "printf encode/decode tests");
+
+	for (i = 0; printf_tests[i].data; i++) {
+		const struct printf_test_data *test = &printf_tests[i];
+		printf_encode(buf, sizeof(buf), test->data, test->len);
+		wpa_printf(MSG_INFO, "%d: -> \"%s\"", i, buf);
+
+		binlen = printf_decode(bin, sizeof(bin), buf);
+		if (binlen != test->len ||
+		    os_memcmp(bin, test->data, binlen) != 0) {
+			wpa_hexdump(MSG_ERROR, "Error in decoding#1",
+				    bin, binlen);
+			errors++;
+		}
+
+		binlen = printf_decode(bin, sizeof(bin), test->encoded);
+		if (binlen != test->len ||
+		    os_memcmp(bin, test->data, binlen) != 0) {
+			wpa_hexdump(MSG_ERROR, "Error in decoding#2",
+				    bin, binlen);
+			errors++;
+		}
+	}
+
+	buf[5] = 'A';
+	printf_encode(buf, 5, (const u8 *) "abcde", 5);
+	if (buf[5] != 'A') {
+		wpa_printf(MSG_ERROR, "Error in bounds checking#1");
+		errors++;
+	}
+
+	for (i = 5; i < 10; i++) {
+		buf[i] = 'A';
+		printf_encode(buf, i, (const u8 *) "\xdd\xdd\xdd\xdd\xdd", 5);
+		if (buf[i] != 'A') {
+			wpa_printf(MSG_ERROR, "Error in bounds checking#2(%d)",
+				   i);
+			errors++;
+		}
+	}
+
+	if (printf_decode(bin, 3, "abcde") != 2)
+		errors++;
+
+	if (printf_decode(bin, 3, "\\xa") != 1 || bin[0] != 10)
+		errors++;
+
+	if (printf_decode(bin, 3, "\\xq") != 1 || bin[0] != 'q')
+		errors++;
+
+	if (printf_decode(bin, 3, "\\a") != 1 || bin[0] != 'a')
+		errors++;
+
+	array[0] = 10;
+	array[1] = 10;
+	array[2] = 5;
+	array[3] = 10;
+	array[4] = 5;
+	array[5] = 0;
+	if (int_array_len(array) != 5)
+		errors++;
+	int_array_sort_unique(array);
+	if (int_array_len(array) != 2)
+		errors++;
+
+	if (errors) {
+		wpa_printf(MSG_ERROR, "%d printf test(s) failed", errors);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int bitfield_tests(void)
+{
+	struct bitfield *bf;
+	int i;
+	int errors = 0;
+
+	wpa_printf(MSG_INFO, "bitfield tests");
+
+	bf = bitfield_alloc(123);
+	if (bf == NULL)
+		return -1;
+
+	for (i = 0; i < 123; i++) {
+		if (bitfield_is_set(bf, i) || bitfield_is_set(bf, i + 1))
+			errors++;
+		if (i > 0 && bitfield_is_set(bf, i - 1))
+			errors++;
+		bitfield_set(bf, i);
+		if (!bitfield_is_set(bf, i))
+			errors++;
+		bitfield_clear(bf, i);
+		if (bitfield_is_set(bf, i))
+			errors++;
+	}
+
+	for (i = 123; i < 200; i++) {
+		if (bitfield_is_set(bf, i) || bitfield_is_set(bf, i + 1))
+			errors++;
+		if (i > 0 && bitfield_is_set(bf, i - 1))
+			errors++;
+		bitfield_set(bf, i);
+		if (bitfield_is_set(bf, i))
+			errors++;
+		bitfield_clear(bf, i);
+		if (bitfield_is_set(bf, i))
+			errors++;
+	}
+
+	for (i = 0; i < 123; i++) {
+		if (bitfield_is_set(bf, i) || bitfield_is_set(bf, i + 1))
+			errors++;
+		bitfield_set(bf, i);
+		if (!bitfield_is_set(bf, i))
+			errors++;
+	}
+
+	for (i = 0; i < 123; i++) {
+		if (!bitfield_is_set(bf, i))
+			errors++;
+		bitfield_clear(bf, i);
+		if (bitfield_is_set(bf, i))
+			errors++;
+	}
+
+	for (i = 0; i < 123; i++) {
+		if (bitfield_get_first_zero(bf) != i)
+			errors++;
+		bitfield_set(bf, i);
+	}
+	if (bitfield_get_first_zero(bf) != -1)
+		errors++;
+	for (i = 0; i < 123; i++) {
+		if (!bitfield_is_set(bf, i))
+			errors++;
+		bitfield_clear(bf, i);
+		if (bitfield_get_first_zero(bf) != i)
+			errors++;
+		bitfield_set(bf, i);
+	}
+	if (bitfield_get_first_zero(bf) != -1)
+		errors++;
+
+	bitfield_free(bf);
+
+	bf = bitfield_alloc(8);
+	if (bf == NULL)
+		return -1;
+	if (bitfield_get_first_zero(bf) != 0)
+		errors++;
+	for (i = 0; i < 8; i++)
+		bitfield_set(bf, i);
+	if (bitfield_get_first_zero(bf) != -1)
+		errors++;
+	bitfield_free(bf);
+
+	if (errors) {
+		wpa_printf(MSG_ERROR, "%d bitfield test(s) failed", errors);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int int_array_tests(void)
+{
+	int test1[] = { 1, 2, 3, 4, 5, 6, 0 };
+	int test2[] = { 1, -1, 0 };
+	int test3[] = { 1, 1, 1, -1, 2, 3, 4, 1, 2, 0 };
+	int test3_res[] = { -1, 1, 2, 3, 4, 0 };
+	int errors = 0;
+	size_t len;
+
+	wpa_printf(MSG_INFO, "int_array tests");
+
+	if (int_array_len(test1) != 6 ||
+	    int_array_len(test2) != 2)
+		errors++;
+
+	int_array_sort_unique(test3);
+	len = int_array_len(test3_res);
+	if (int_array_len(test3) != len)
+		errors++;
+	else if (os_memcmp(test3, test3_res, len * sizeof(int)) != 0)
+		errors++;
+
+	if (errors) {
+		wpa_printf(MSG_ERROR, "%d int_array test(s) failed", errors);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int ext_password_tests(void)
+{
+	struct ext_password_data *data;
+	int ret = 0;
+	struct wpabuf *pw;
+
+	wpa_printf(MSG_INFO, "ext_password tests");
+
+	data = ext_password_init("unknown", "foo");
+	if (data != NULL)
+		return -1;
+
+	data = ext_password_init("test", NULL);
+	if (data == NULL)
+		return -1;
+	pw = ext_password_get(data, "foo");
+	if (pw != NULL)
+		ret = -1;
+	ext_password_free(pw);
+
+	ext_password_deinit(data);
+
+	pw = ext_password_get(NULL, "foo");
+	if (pw != NULL)
+		ret = -1;
+	ext_password_free(pw);
+
+	return ret;
+}
+
+
+static int trace_tests(void)
+{
+	wpa_printf(MSG_INFO, "trace tests");
+
+	wpa_trace_show("test backtrace");
+	wpa_trace_dump_funcname("test funcname", trace_tests);
+
+	return 0;
+}
+
+
+static int base64_tests(void)
+{
+	int errors = 0;
+	unsigned char *res;
+	char *res2;
+	size_t res_len;
+
+	wpa_printf(MSG_INFO, "base64 tests");
+
+	res2 = base64_encode("", ~0, &res_len);
+	if (res2) {
+		errors++;
+		os_free(res2);
+	}
+
+	res2 = base64_encode("=", 1, &res_len);
+	if (!res2 || res_len != 5 || res2[0] != 'P' || res2[1] != 'Q' ||
+	    res2[2] != '=' || res2[3] != '=' || res2[4] != '\n')
+		errors++;
+	os_free(res2);
+
+	res2 = base64_encode("=", 1, NULL);
+	if (!res2 || res2[0] != 'P' || res2[1] != 'Q' ||
+	    res2[2] != '=' || res2[3] != '=' || res2[4] != '\n')
+		errors++;
+	os_free(res2);
+
+	res = base64_decode("", 0, &res_len);
+	if (res) {
+		errors++;
+		os_free(res);
+	}
+
+	res = base64_decode("a", 1, &res_len);
+	if (res) {
+		errors++;
+		os_free(res);
+	}
+
+	res = base64_decode("====", 4, &res_len);
+	if (res) {
+		errors++;
+		os_free(res);
+	}
+
+	res = base64_decode("PQ==", 4, &res_len);
+	if (!res || res_len != 1 || res[0] != '=')
+		errors++;
+	os_free(res);
+
+	res = base64_decode("P.Q-=!=*", 8, &res_len);
+	if (!res || res_len != 1 || res[0] != '=')
+		errors++;
+	os_free(res);
+
+	if (errors) {
+		wpa_printf(MSG_ERROR, "%d base64 test(s) failed", errors);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int common_tests(void)
+{
+	char buf[3], longbuf[100];
+	u8 addr[ETH_ALEN] = { 1, 2, 3, 4, 5, 6 };
+	u8 bin[3];
+	int errors = 0;
+	struct wpa_freq_range_list ranges;
+	size_t len;
+	const char *txt;
+	u8 ssid[255];
+
+	wpa_printf(MSG_INFO, "common tests");
+
+	if (hwaddr_mask_txt(buf, 3, addr, addr) != -1)
+		errors++;
+
+	if (wpa_scnprintf(buf, 0, "hello") != 0 ||
+	    wpa_scnprintf(buf, 3, "hello") != 2)
+		errors++;
+
+	if (wpa_snprintf_hex(buf, 0, addr, ETH_ALEN) != 0 ||
+	    wpa_snprintf_hex(buf, 3, addr, ETH_ALEN) != 2)
+		errors++;
+
+	if (merge_byte_arrays(bin, 3, addr, ETH_ALEN, NULL, 0) != 3 ||
+	    merge_byte_arrays(bin, 3, NULL, 0, addr, ETH_ALEN) != 3)
+		errors++;
+
+	if (dup_binstr(NULL, 0) != NULL)
+		errors++;
+
+	if (freq_range_list_includes(NULL, 0) != 0)
+		errors++;
+
+	os_memset(&ranges, 0, sizeof(ranges));
+	if (freq_range_list_parse(&ranges, "") != 0 ||
+	    freq_range_list_includes(&ranges, 0) != 0 ||
+	    freq_range_list_str(&ranges) != NULL)
+		errors++;
+
+	if (utf8_unescape(NULL, 0, buf, sizeof(buf)) != 0 ||
+	    utf8_unescape("a", 1, NULL, 0) != 0 ||
+	    utf8_unescape("a\\", 2, buf, sizeof(buf)) != 0 ||
+	    utf8_unescape("abcde", 5, buf, sizeof(buf)) != 0 ||
+	    utf8_unescape("abc", 3, buf, 3) != 3)
+		errors++;
+
+	if (utf8_unescape("a", 0, buf, sizeof(buf)) != 1 || buf[0] != 'a')
+		errors++;
+
+	if (utf8_unescape("\\b", 2, buf, sizeof(buf)) != 1 || buf[0] != 'b')
+		errors++;
+
+	if (utf8_escape(NULL, 0, buf, sizeof(buf)) != 0 ||
+	    utf8_escape("a", 1, NULL, 0) != 0 ||
+	    utf8_escape("abcde", 5, buf, sizeof(buf)) != 0 ||
+	    utf8_escape("a\\bcde", 6, buf, sizeof(buf)) != 0 ||
+	    utf8_escape("ab\\cde", 6, buf, sizeof(buf)) != 0 ||
+	    utf8_escape("abc\\de", 6, buf, sizeof(buf)) != 0 ||
+	    utf8_escape("abc", 3, buf, 3) != 3)
+		errors++;
+
+	if (utf8_escape("a", 0, buf, sizeof(buf)) != 1 || buf[0] != 'a')
+		errors++;
+
+	os_memset(ssid, 0, sizeof(ssid));
+	txt = wpa_ssid_txt(ssid, sizeof(ssid));
+	len = os_strlen(txt);
+	/* Verify that SSID_MAX_LEN * 4 buffer limit is enforced. */
+	if (len != SSID_MAX_LEN * 4) {
+		wpa_printf(MSG_ERROR,
+			   "Unexpected wpa_ssid_txt() result with too long SSID");
+		errors++;
+	}
+
+	if (wpa_snprintf_hex_sep(longbuf, 0, addr, ETH_ALEN, '-') != 0 ||
+	    wpa_snprintf_hex_sep(longbuf, 5, addr, ETH_ALEN, '-') != 3 ||
+	    os_strcmp(longbuf, "01-0") != 0)
+		errors++;
+
+	if (errors) {
+		wpa_printf(MSG_ERROR, "%d common test(s) failed", errors);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int os_tests(void)
+{
+	int errors = 0;
+	void *ptr;
+	os_time_t t;
+
+	wpa_printf(MSG_INFO, "os tests");
+
+	ptr = os_calloc((size_t) -1, (size_t) -1);
+	if (ptr) {
+		errors++;
+		os_free(ptr);
+	}
+	ptr = os_calloc((size_t) 2, (size_t) -1);
+	if (ptr) {
+		errors++;
+		os_free(ptr);
+	}
+	ptr = os_calloc((size_t) -1, (size_t) 2);
+	if (ptr) {
+		errors++;
+		os_free(ptr);
+	}
+
+	ptr = os_realloc_array(NULL, (size_t) -1, (size_t) -1);
+	if (ptr) {
+		errors++;
+		os_free(ptr);
+	}
+
+	os_sleep(1, 1);
+
+	if (os_mktime(1969, 1, 1, 1, 1, 1, &t) == 0 ||
+	    os_mktime(1971, 0, 1, 1, 1, 1, &t) == 0 ||
+	    os_mktime(1971, 13, 1, 1, 1, 1, &t) == 0 ||
+	    os_mktime(1971, 1, 0, 1, 1, 1, &t) == 0 ||
+	    os_mktime(1971, 1, 32, 1, 1, 1, &t) == 0 ||
+	    os_mktime(1971, 1, 1, -1, 1, 1, &t) == 0 ||
+	    os_mktime(1971, 1, 1, 24, 1, 1, &t) == 0 ||
+	    os_mktime(1971, 1, 1, 1, -1, 1, &t) == 0 ||
+	    os_mktime(1971, 1, 1, 1, 60, 1, &t) == 0 ||
+	    os_mktime(1971, 1, 1, 1, 1, -1, &t) == 0 ||
+	    os_mktime(1971, 1, 1, 1, 1, 61, &t) == 0 ||
+	    os_mktime(1971, 1, 1, 1, 1, 1, &t) != 0 ||
+	    os_mktime(2020, 1, 2, 3, 4, 5, &t) != 0 ||
+	    os_mktime(2015, 12, 31, 23, 59, 59, &t) != 0)
+		errors++;
+
+	if (os_setenv("hwsim_test_env", "test value", 0) != 0 ||
+	    os_setenv("hwsim_test_env", "test value 2", 1) != 0 ||
+	    os_unsetenv("hwsim_test_env") != 0)
+		errors++;
+
+	if (os_file_exists("/this-file-does-not-exists-hwsim") != 0)
+		errors++;
+
+	if (errors) {
+		wpa_printf(MSG_ERROR, "%d os test(s) failed", errors);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int wpabuf_tests(void)
+{
+	int errors = 0;
+	void *ptr;
+	struct wpabuf *buf;
+
+	wpa_printf(MSG_INFO, "wpabuf tests");
+
+	ptr = os_malloc(100);
+	if (ptr) {
+		buf = wpabuf_alloc_ext_data(ptr, 100);
+		if (buf) {
+			if (wpabuf_resize(&buf, 100) < 0)
+				errors++;
+			else
+				wpabuf_put(buf, 100);
+			wpabuf_free(buf);
+		} else {
+			errors++;
+			os_free(ptr);
+		}
+	} else {
+		errors++;
+	}
+
+	buf = wpabuf_alloc(100);
+	if (buf) {
+		struct wpabuf *buf2;
+
+		wpabuf_put(buf, 100);
+		if (wpabuf_resize(&buf, 100) < 0)
+			errors++;
+		else
+			wpabuf_put(buf, 100);
+		buf2 = wpabuf_concat(buf, NULL);
+		if (buf2 != buf)
+			errors++;
+		wpabuf_free(buf2);
+	} else {
+		errors++;
+	}
+
+	buf = NULL;
+	buf = wpabuf_zeropad(buf, 10);
+	if (buf != NULL)
+		errors++;
+
+	if (errors) {
+		wpa_printf(MSG_ERROR, "%d wpabuf test(s) failed", errors);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int ip_addr_tests(void)
+{
+	int errors = 0;
+	struct hostapd_ip_addr addr;
+	char buf[100];
+
+	wpa_printf(MSG_INFO, "ip_addr tests");
+
+	if (hostapd_parse_ip_addr("1.2.3.4", &addr) != 0 ||
+	    addr.af != AF_INET ||
+	    hostapd_ip_txt(NULL, buf, sizeof(buf)) != NULL ||
+	    hostapd_ip_txt(&addr, buf, 1) != buf || buf[0] != '\0' ||
+	    hostapd_ip_txt(&addr, buf, 0) != NULL ||
+	    hostapd_ip_txt(&addr, buf, sizeof(buf)) != buf)
+		errors++;
+
+	if (hostapd_parse_ip_addr("::", &addr) != 0 ||
+	    addr.af != AF_INET6 ||
+	    hostapd_ip_txt(&addr, buf, 1) != buf || buf[0] != '\0' ||
+	    hostapd_ip_txt(&addr, buf, sizeof(buf)) != buf)
+		errors++;
+
+	if (errors) {
+		wpa_printf(MSG_ERROR, "%d ip_addr test(s) failed", errors);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+struct test_eloop {
+	unsigned int magic;
+	int close_in_timeout;
+	int pipefd1[2];
+	int pipefd2[2];
+};
+
+
+static void eloop_tests_start(int close_in_timeout);
+
+
+static void eloop_test_read_2(int sock, void *eloop_ctx, void *sock_ctx)
+{
+	struct test_eloop *t = eloop_ctx;
+	ssize_t res;
+	char buf[10];
+
+	wpa_printf(MSG_INFO, "%s: sock=%d", __func__, sock);
+
+	if (t->magic != 0x12345678) {
+		wpa_printf(MSG_INFO, "%s: unexpected magic 0x%x",
+			   __func__, t->magic);
+	}
+
+	if (t->pipefd2[0] != sock) {
+		wpa_printf(MSG_INFO, "%s: unexpected sock %d != %d",
+			   __func__, sock, t->pipefd2[0]);
+	}
+
+	res = read(sock, buf, sizeof(buf));
+	wpa_printf(MSG_INFO, "%s: sock=%d --> res=%d",
+		   __func__, sock, (int) res);
+}
+
+
+static void eloop_test_read_2_wrong(int sock, void *eloop_ctx, void *sock_ctx)
+{
+	struct test_eloop *t = eloop_ctx;
+
+	wpa_printf(MSG_INFO, "%s: sock=%d", __func__, sock);
+
+	if (t->magic != 0x12345678) {
+		wpa_printf(MSG_INFO, "%s: unexpected magic 0x%x",
+			   __func__, t->magic);
+	}
+
+	if (t->pipefd2[0] != sock) {
+		wpa_printf(MSG_INFO, "%s: unexpected sock %d != %d",
+			   __func__, sock, t->pipefd2[0]);
+	}
+
+	/*
+	 * This is expected to block due to the original socket with data having
+	 * been closed and no new data having been written to the new socket
+	 * with the same fd. To avoid blocking the process during test, skip the
+	 * read here.
+	 */
+	wpa_printf(MSG_ERROR, "%s: FAIL - should not have called this function",
+		   __func__);
+}
+
+
+static void reopen_pipefd2(struct test_eloop *t)
+{
+	if (t->pipefd2[0] < 0) {
+		wpa_printf(MSG_INFO, "pipefd2 had been closed");
+	} else {
+		int res;
+
+		wpa_printf(MSG_INFO, "close pipefd2");
+		eloop_unregister_read_sock(t->pipefd2[0]);
+		close(t->pipefd2[0]);
+		t->pipefd2[0] = -1;
+		close(t->pipefd2[1]);
+		t->pipefd2[1] = -1;
+
+		res = pipe(t->pipefd2);
+		if (res < 0) {
+			wpa_printf(MSG_INFO, "pipe: %s", strerror(errno));
+			t->pipefd2[0] = -1;
+			t->pipefd2[1] = -1;
+			return;
+		}
+
+		wpa_printf(MSG_INFO,
+			   "re-register pipefd2 with new sockets %d,%d",
+			   t->pipefd2[0], t->pipefd2[1]);
+		eloop_register_read_sock(t->pipefd2[0], eloop_test_read_2_wrong,
+					 t, NULL);
+	}
+}
+
+
+static void eloop_test_read_1(int sock, void *eloop_ctx, void *sock_ctx)
+{
+	struct test_eloop *t = eloop_ctx;
+	ssize_t res;
+	char buf[10];
+
+	wpa_printf(MSG_INFO, "%s: sock=%d", __func__, sock);
+
+	if (t->magic != 0x12345678) {
+		wpa_printf(MSG_INFO, "%s: unexpected magic 0x%x",
+			   __func__, t->magic);
+	}
+
+	if (t->pipefd1[0] != sock) {
+		wpa_printf(MSG_INFO, "%s: unexpected sock %d != %d",
+			   __func__, sock, t->pipefd1[0]);
+	}
+
+	res = read(sock, buf, sizeof(buf));
+	wpa_printf(MSG_INFO, "%s: sock=%d --> res=%d",
+		   __func__, sock, (int) res);
+
+	if (!t->close_in_timeout)
+		reopen_pipefd2(t);
+}
+
+
+static void eloop_test_cb(void *eloop_data, void *user_ctx)
+{
+	struct test_eloop *t = eloop_data;
+
+	wpa_printf(MSG_INFO, "%s", __func__);
+
+	if (t->magic != 0x12345678) {
+		wpa_printf(MSG_INFO, "%s: unexpected magic 0x%x",
+			   __func__, t->magic);
+	}
+
+	if (t->close_in_timeout)
+		reopen_pipefd2(t);
+}
+
+
+static void eloop_test_timeout(void *eloop_data, void *user_ctx)
+{
+	struct test_eloop *t = eloop_data;
+	int next_run = 0;
+
+	wpa_printf(MSG_INFO, "%s", __func__);
+
+	if (t->magic != 0x12345678) {
+		wpa_printf(MSG_INFO, "%s: unexpected magic 0x%x",
+			   __func__, t->magic);
+	}
+
+	if (t->pipefd1[0] >= 0) {
+		wpa_printf(MSG_INFO, "pipefd1 had not been closed");
+		eloop_unregister_read_sock(t->pipefd1[0]);
+		close(t->pipefd1[0]);
+		t->pipefd1[0] = -1;
+		close(t->pipefd1[1]);
+		t->pipefd1[1] = -1;
+	}
+
+	if (t->pipefd2[0] >= 0) {
+		wpa_printf(MSG_INFO, "pipefd2 had not been closed");
+		eloop_unregister_read_sock(t->pipefd2[0]);
+		close(t->pipefd2[0]);
+		t->pipefd2[0] = -1;
+		close(t->pipefd2[1]);
+		t->pipefd2[1] = -1;
+	}
+
+	next_run = t->close_in_timeout;
+	t->magic = 0;
+	wpa_printf(MSG_INFO, "%s - free(%p)", __func__, t);
+	os_free(t);
+
+	if (next_run)
+		eloop_tests_start(0);
+}
+
+
+static void eloop_tests_start(int close_in_timeout)
+{
+	struct test_eloop *t;
+	int res;
+
+	t = os_zalloc(sizeof(*t));
+	if (!t)
+		return;
+	t->magic = 0x12345678;
+	t->close_in_timeout = close_in_timeout;
+
+	wpa_printf(MSG_INFO, "starting eloop tests (%p) (close_in_timeout=%d)",
+		   t, close_in_timeout);
+
+	res = pipe(t->pipefd1);
+	if (res < 0) {
+		wpa_printf(MSG_INFO, "pipe: %s", strerror(errno));
+		os_free(t);
+		return;
+	}
+
+	res = pipe(t->pipefd2);
+	if (res < 0) {
+		wpa_printf(MSG_INFO, "pipe: %s", strerror(errno));
+		close(t->pipefd1[0]);
+		close(t->pipefd1[1]);
+		os_free(t);
+		return;
+	}
+
+	wpa_printf(MSG_INFO, "pipe fds: %d,%d %d,%d",
+		   t->pipefd1[0], t->pipefd1[1],
+		   t->pipefd2[0], t->pipefd2[1]);
+
+	eloop_register_read_sock(t->pipefd1[0], eloop_test_read_1, t, NULL);
+	eloop_register_read_sock(t->pipefd2[0], eloop_test_read_2, t, NULL);
+	eloop_register_timeout(0, 0, eloop_test_cb, t, NULL);
+	eloop_register_timeout(0, 200000, eloop_test_timeout, t, NULL);
+
+	if (write(t->pipefd1[1], "HELLO", 5) < 0)
+		wpa_printf(MSG_INFO, "write: %s", strerror(errno));
+	if (write(t->pipefd2[1], "TEST", 4) < 0)
+		wpa_printf(MSG_INFO, "write: %s", strerror(errno));
+	os_sleep(0, 50000);
+	wpa_printf(MSG_INFO, "waiting for eloop callbacks");
+}
+
+
+static void eloop_tests_run(void *eloop_data, void *user_ctx)
+{
+	eloop_tests_start(1);
+}
+
+
+static int eloop_tests(void)
+{
+	wpa_printf(MSG_INFO, "schedule eloop tests to be run");
+
+	/*
+	 * Cannot return error from these without a significant design change,
+	 * so for now, run the tests from a scheduled timeout and require
+	 * separate verification of the results from the debug log.
+	 */
+	eloop_register_timeout(0, 0, eloop_tests_run, NULL, NULL);
+
+	return 0;
+}
+
+
+#ifdef CONFIG_JSON
+struct json_test_data {
+	const char *json;
+	const char *tree;
+};
+
+static const struct json_test_data json_test_cases[] = {
+	{ "{}", "[1:OBJECT:]" },
+	{ "[]", "[1:ARRAY:]" },
+	{ "{", NULL },
+	{ "[", NULL },
+	{ "}", NULL },
+	{ "]", NULL },
+	{ "[[]]", "[1:ARRAY:][2:ARRAY:]" },
+	{ "{\"t\":\"test\"}", "[1:OBJECT:][2:STRING:t]" },
+	{ "{\"t\":123}", "[1:OBJECT:][2:NUMBER:t]" },
+	{ "{\"t\":true}", "[1:OBJECT:][2:BOOLEAN:t]" },
+	{ "{\"t\":false}", "[1:OBJECT:][2:BOOLEAN:t]" },
+	{ "{\"t\":null}", "[1:OBJECT:][2:NULL:t]" },
+	{ "{\"t\":truetrue}", NULL },
+	{ "\"test\"", "[1:STRING:]" },
+	{ "123", "[1:NUMBER:]" },
+	{ "true", "[1:BOOLEAN:]" },
+	{ "false", "[1:BOOLEAN:]" },
+	{ "null", "[1:NULL:]" },
+	{ "truetrue", NULL },
+	{ " {\t\n\r\"a\"\n:\r1\n,\n\"b\":3\n}\n",
+	  "[1:OBJECT:][2:NUMBER:a][2:NUMBER:b]" },
+	{ ",", NULL },
+	{ "{,}", NULL },
+	{ "[,]", NULL },
+	{ ":", NULL },
+	{ "{:}", NULL },
+	{ "[:]", NULL },
+	{ "{ \"\\u005c\" : \"\\u005c\" }", "[1:OBJECT:][2:STRING:\\]" },
+	{ "[{},{}]", "[1:ARRAY:][2:OBJECT:][2:OBJECT:]" },
+	{ "[1,2]", "[1:ARRAY:][2:NUMBER:][2:NUMBER:]" },
+	{ "[\"1\",\"2\"]", "[1:ARRAY:][2:STRING:][2:STRING:]" },
+	{ "[true,false]", "[1:ARRAY:][2:BOOLEAN:][2:BOOLEAN:]" },
+};
+#endif /* CONFIG_JSON */
+
+
+static int json_tests(void)
+{
+#ifdef CONFIG_JSON
+	unsigned int i;
+	struct json_token *root;
+	char buf[1000];
+
+	wpa_printf(MSG_INFO, "JSON tests");
+
+	for (i = 0; i < ARRAY_SIZE(json_test_cases); i++) {
+		const struct json_test_data *test = &json_test_cases[i];
+		int res = 0;
+
+		root = json_parse(test->json, os_strlen(test->json));
+		if ((root && !test->tree) || (!root && test->tree)) {
+			wpa_printf(MSG_INFO, "JSON test %u failed", i);
+			res = -1;
+		} else if (root) {
+			json_print_tree(root, buf, sizeof(buf));
+			if (os_strcmp(buf, test->tree) != 0) {
+				wpa_printf(MSG_INFO,
+					   "JSON test %u tree mismatch: %s %s",
+					   i, buf, test->tree);
+				res = -1;
+			}
+		}
+		json_free(root);
+		if (res < 0)
+			return -1;
+
+	}
+#endif /* CONFIG_JSON */
+	return 0;
+}
+
+
+static int const_time_tests(void)
+{
+	struct const_time_fill_msb_test {
+		unsigned int val;
+		unsigned int expected;
+	} const_time_fill_msb_tests[] = {
+		{ 0, 0 },
+		{ 1, 0 },
+		{ 2, 0 },
+		{ 1U << (sizeof(unsigned int) * 8 - 1), ~0 },
+		{ ~0 - 1, ~0 },
+		{ ~0, ~0 }
+	};
+	struct const_time_is_zero_test {
+		unsigned int val;
+		unsigned int expected;
+	} const_time_is_zero_tests[] = {
+		{ 0, ~0 },
+		{ 1, 0 },
+		{ 2, 0 },
+		{ 1U << (sizeof(unsigned int) * 8 - 1), 0 },
+		{ ~0 - 1, 0 },
+		{ ~0, 0 }
+	};
+	struct const_time_eq_test {
+		unsigned int a;
+		unsigned int b;
+		unsigned int expected;
+		unsigned int expected_u8;
+	} const_time_eq_tests[] = {
+		{ 0, 1, 0, 0 },
+		{ 1, 2, 0, 0 },
+		{ 1, 1, ~0, 0xff },
+		{ ~0, ~0, ~0, 0xff },
+		{ ~0, ~0 - 1, 0, 0 },
+		{ 0, 0, ~0, 0xff }
+	};
+	struct const_time_eq_bin_test {
+		u8 *a;
+		u8 *b;
+		size_t len;
+		unsigned int expected;
+	} const_time_eq_bin_tests[] = {
+		{ (u8 *) "", (u8 *) "", 0, ~0 },
+		{ (u8 *) "abcde", (u8 *) "abcde", 5, ~0 },
+		{ (u8 *) "abcde", (u8 *) "Abcde", 5, 0 },
+		{ (u8 *) "abcde", (u8 *) "aBcde", 5, 0 },
+		{ (u8 *) "abcde", (u8 *) "abCde", 5, 0 },
+		{ (u8 *) "abcde", (u8 *) "abcDe", 5, 0 },
+		{ (u8 *) "abcde", (u8 *) "abcdE", 5, 0 },
+		{ (u8 *) "\x00", (u8 *) "\x01", 1, 0 },
+		{ (u8 *) "\x00", (u8 *) "\x80", 1, 0 },
+		{ (u8 *) "\x00", (u8 *) "\x00", 1, ~0 }
+	};
+	struct const_time_select_test {
+		unsigned int mask;
+		unsigned int true_val;
+		unsigned int false_val;
+		unsigned int expected;
+	} const_time_select_tests[] = {
+		{ ~0, ~0, ~0, ~0 },
+		{ 0, ~0, ~0, ~0 },
+		{ ~0, ~0, 0, ~0 },
+		{ 0, ~0, 0, 0 },
+		{ ~0, 0xaaaaaaaa, 0x55555555, 0xaaaaaaaa },
+		{ 0, 0xaaaaaaaa, 0x55555555, 0x55555555 },
+		{ ~0, 3, 3, 3 },
+		{ 0, 3, 3, 3 },
+		{ ~0, 1, 2, 1 },
+		{ 0, 1, 2, 2 }
+	};
+	struct const_time_select_int_test {
+		unsigned int mask;
+		int true_val;
+		int false_val;
+		int expected;
+	} const_time_select_int_tests[] = {
+		{ ~0, -128, 127, -128 },
+		{ 0, -128, 127, 127 },
+		{ ~0, -2147483648, 2147483647, -2147483648 },
+		{ 0, -2147483648, 2147483647, 2147483647 },
+		{ ~0, 0, 0, 0 },
+		{ 0, 0, 0, 0 },
+		{ ~0, -1, 1, -1 },
+		{ 0, -1, 1, 1 }
+	};
+	struct const_time_select_u8_test {
+		u8 mask;
+		u8 true_val;
+		u8 false_val;
+		u8 expected;
+	} const_time_select_u8_tests[] = {
+		{ ~0, ~0, ~0, ~0 },
+		{ 0, ~0, ~0, ~0 },
+		{ ~0, ~0, 0, ~0 },
+		{ 0, ~0, 0, 0 },
+		{ ~0, 0xaa, 0x55, 0xaa },
+		{ 0, 0xaa, 0x55, 0x55 },
+		{ ~0, 1, 2, 1 },
+		{ 0, 1, 2, 2 }
+	};
+	struct const_time_select_s8_test {
+		u8 mask;
+		s8 true_val;
+		s8 false_val;
+		s8 expected;
+	} const_time_select_s8_tests[] = {
+		{ ~0, -128, 127, -128 },
+		{ 0, -128, 127, 127 },
+		{ ~0, 0, 0, 0 },
+		{ 0, 0, 0, 0 },
+		{ ~0, -1, 1, -1 },
+		{ 0, -1, 1, 1 }
+	};
+	struct const_time_select_bin_test {
+		u8 mask;
+		u8 *true_val;
+		u8 *false_val;
+		size_t len;
+		u8 *expected;
+	} const_time_select_bin_tests[] = {
+		{ ~0, (u8 *) "abcde", (u8 *) "ABCDE", 5, (u8 *) "abcde" },
+		{ 0, (u8 *) "abcde", (u8 *) "ABCDE", 5, (u8 *) "ABCDE" },
+		{ ~0, (u8 *) "", (u8 *) "", 0, (u8 *) "" },
+		{ 0, (u8 *) "", (u8 *) "", 0, (u8 *) "" }
+	};
+	struct const_time_memcmp_test {
+		char *a;
+		char *b;
+		size_t len;
+		int expected;
+	} const_time_memcmp_tests[] = {
+		{ "abcde", "abcde", 5, 0 },
+		{ "abcde", "bbcde", 5, -1 },
+		{ "bbcde", "abcde", 5, 1 },
+		{ "accde", "abcde", 5, 1 },
+		{ "abcee", "abcde", 5, 1 },
+		{ "abcdf", "abcde", 5, 1 },
+		{ "cbcde", "aXXXX", 5, 2 },
+		{ "a", "d", 1, -3 },
+		{ "", "", 0, 0 }
+	};
+	unsigned int i;
+	int ret = 0;
+
+	wpa_printf(MSG_INFO, "constant time tests");
+
+	for (i = 0; i < ARRAY_SIZE(const_time_fill_msb_tests); i++) {
+		struct const_time_fill_msb_test *test;
+
+		test = &const_time_fill_msb_tests[i];
+		if (const_time_fill_msb(test->val) != test->expected) {
+			wpa_printf(MSG_ERROR,
+				   "const_time_fill_msb(0x%x) test failed",
+				   test->val);
+			ret = -1;
+		}
+	}
+
+	for (i = 0; i < ARRAY_SIZE(const_time_is_zero_tests); i++) {
+		struct const_time_is_zero_test *test;
+
+		test = &const_time_is_zero_tests[i];
+		if (const_time_is_zero(test->val) != test->expected) {
+			wpa_printf(MSG_ERROR,
+				   "const_time_is_zero(0x%x) test failed",
+				   test->val);
+			ret = -1;
+		}
+	}
+
+	for (i = 0; i < ARRAY_SIZE(const_time_eq_tests); i++) {
+		struct const_time_eq_test *test;
+
+		test = &const_time_eq_tests[i];
+		if (const_time_eq(test->a, test->b) != test->expected) {
+			wpa_printf(MSG_ERROR,
+				   "const_time_eq(0x%x,0x%x) test failed",
+				   test->a, test->b);
+			ret = -1;
+		}
+		if (const_time_eq_u8(test->a, test->b) != test->expected_u8) {
+			wpa_printf(MSG_ERROR,
+				   "const_time_eq_u8(0x%x,0x%x) test failed",
+				   test->a, test->b);
+			ret = -1;
+		}
+	}
+
+	for (i = 0; i < ARRAY_SIZE(const_time_eq_bin_tests); i++) {
+		struct const_time_eq_bin_test *test;
+
+		test = &const_time_eq_bin_tests[i];
+		if (const_time_eq_bin(test->a, test->b, test->len) !=
+		    test->expected) {
+			wpa_printf(MSG_ERROR,
+				   "const_time_eq_bin(len=%u) test failed",
+				   (unsigned int) test->len);
+			ret = -1;
+		}
+	}
+
+	for (i = 0; i < ARRAY_SIZE(const_time_select_tests); i++) {
+		struct const_time_select_test *test;
+
+		test = &const_time_select_tests[i];
+		if (const_time_select(test->mask, test->true_val,
+				      test->false_val) != test->expected) {
+			wpa_printf(MSG_ERROR,
+				   "const_time_select(0x%x,0x%x,0x%x) test failed",
+				   test->mask, test->true_val, test->false_val);
+			ret = -1;
+		}
+	}
+
+	for (i = 0; i < ARRAY_SIZE(const_time_select_int_tests); i++) {
+		struct const_time_select_int_test *test;
+
+		test = &const_time_select_int_tests[i];
+		if (const_time_select_int(test->mask, test->true_val,
+					  test->false_val) != test->expected) {
+			wpa_printf(MSG_ERROR,
+				   "const_time_select_int(0x%x,%d,%d) test failed",
+				   test->mask, test->true_val, test->false_val);
+			ret = -1;
+		}
+	}
+
+	for (i = 0; i < ARRAY_SIZE(const_time_select_u8_tests); i++) {
+		struct const_time_select_u8_test *test;
+
+		test = &const_time_select_u8_tests[i];
+		if (const_time_select_u8(test->mask, test->true_val,
+					 test->false_val) != test->expected) {
+			wpa_printf(MSG_ERROR,
+				   "const_time_select_u8(0x%x,0x%x,0x%x) test failed",
+				   test->mask, test->true_val, test->false_val);
+			ret = -1;
+		}
+	}
+
+	for (i = 0; i < ARRAY_SIZE(const_time_select_s8_tests); i++) {
+		struct const_time_select_s8_test *test;
+
+		test = &const_time_select_s8_tests[i];
+		if (const_time_select_s8(test->mask, test->true_val,
+					 test->false_val) != test->expected) {
+			wpa_printf(MSG_ERROR,
+				   "const_time_select_s8(0x%x,0x%x,0x%x) test failed",
+				   test->mask, test->true_val, test->false_val);
+			ret = -1;
+		}
+	}
+
+	for (i = 0; i < ARRAY_SIZE(const_time_select_bin_tests); i++) {
+		struct const_time_select_bin_test *test;
+		u8 dst[100];
+
+		test = &const_time_select_bin_tests[i];
+		const_time_select_bin(test->mask, test->true_val,
+				      test->false_val, test->len, dst);
+		if (os_memcmp(dst, test->expected, test->len) != 0) {
+			wpa_printf(MSG_ERROR,
+				   "const_time_select_bin(0x%x,%u) test failed",
+				   test->mask, (unsigned int) test->len);
+			ret = -1;
+		}
+	}
+
+	for (i = 0; i < ARRAY_SIZE(const_time_memcmp_tests); i++) {
+		struct const_time_memcmp_test *test;
+		int res;
+
+		test = &const_time_memcmp_tests[i];
+		res = const_time_memcmp(test->a, test->b, test->len);
+		if (res != test->expected) {
+			wpa_printf(MSG_ERROR,
+				   "const_time_memcmp(%s,%s,%d) test failed (%d != %d)",
+				   test->a, test->b, (int) test->len,
+				   res, test->expected);
+			ret = -1;
+		}
+	}
+
+	return ret;
+}
+
+
+int utils_module_tests(void)
+{
+	int ret = 0;
+
+	wpa_printf(MSG_INFO, "utils module tests");
+
+	if (printf_encode_decode_tests() < 0 ||
+	    ext_password_tests() < 0 ||
+	    trace_tests() < 0 ||
+	    bitfield_tests() < 0 ||
+	    base64_tests() < 0 ||
+	    common_tests() < 0 ||
+	    os_tests() < 0 ||
+	    wpabuf_tests() < 0 ||
+	    ip_addr_tests() < 0 ||
+	    eloop_tests() < 0 ||
+	    json_tests() < 0 ||
+	    const_time_tests() < 0 ||
+	    int_array_tests() < 0)
+		ret = -1;
+
+	return ret;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/uuid.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/uuid.c
new file mode 100644
index 0000000..98e43d0
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/uuid.c
@@ -0,0 +1,96 @@
+/*
+ * Universally Unique IDentifier (UUID)
+ * Copyright (c) 2008, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/sha256.h"
+#include "uuid.h"
+
+int uuid_str2bin(const char *str, u8 *bin)
+{
+	const char *pos;
+	u8 *opos;
+
+	pos = str;
+	opos = bin;
+
+	if (hexstr2bin(pos, opos, 4))
+		return -1;
+	pos += 8;
+	opos += 4;
+
+	if (*pos++ != '-' || hexstr2bin(pos, opos, 2))
+		return -1;
+	pos += 4;
+	opos += 2;
+
+	if (*pos++ != '-' || hexstr2bin(pos, opos, 2))
+		return -1;
+	pos += 4;
+	opos += 2;
+
+	if (*pos++ != '-' || hexstr2bin(pos, opos, 2))
+		return -1;
+	pos += 4;
+	opos += 2;
+
+	if (*pos++ != '-' || hexstr2bin(pos, opos, 6))
+		return -1;
+
+	return 0;
+}
+
+
+int uuid_bin2str(const u8 *bin, char *str, size_t max_len)
+{
+	int len;
+	len = os_snprintf(str, max_len, "%02x%02x%02x%02x-%02x%02x-%02x%02x-"
+			  "%02x%02x-%02x%02x%02x%02x%02x%02x",
+			  bin[0], bin[1], bin[2], bin[3],
+			  bin[4], bin[5], bin[6], bin[7],
+			  bin[8], bin[9], bin[10], bin[11],
+			  bin[12], bin[13], bin[14], bin[15]);
+	if (os_snprintf_error(max_len, len))
+		return -1;
+	return 0;
+}
+
+
+int is_nil_uuid(const u8 *uuid)
+{
+	int i;
+	for (i = 0; i < UUID_LEN; i++)
+		if (uuid[i])
+			return 0;
+	return 1;
+}
+
+
+int uuid_random(u8 *uuid)
+{
+	struct os_time t;
+	u8 hash[SHA256_MAC_LEN];
+
+	/* Use HMAC-SHA256 and timestamp as context to avoid exposing direct
+	 * os_get_random() output in the UUID field. */
+	os_get_time(&t);
+	if (os_get_random(uuid, UUID_LEN) < 0 ||
+	    hmac_sha256(uuid, UUID_LEN, (const u8 *) &t, sizeof(t), hash) < 0)
+		return -1;
+
+	os_memcpy(uuid, hash, UUID_LEN);
+
+	/* Version: 4 = random */
+	uuid[6] = (4 << 4) | (uuid[6] & 0x0f);
+
+	/* Variant specified in RFC 4122 */
+	uuid[8] = 0x80 | (uuid[8] & 0x3f);
+
+	return 0;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/uuid.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/uuid.h
new file mode 100644
index 0000000..6e20210
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/uuid.h
@@ -0,0 +1,19 @@
+/*
+ * Universally Unique IDentifier (UUID)
+ * Copyright (c) 2008, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef UUID_H
+#define UUID_H
+
+#define UUID_LEN 16
+
+int uuid_str2bin(const char *str, u8 *bin);
+int uuid_bin2str(const u8 *bin, char *str, size_t max_len);
+int is_nil_uuid(const u8 *uuid);
+int uuid_random(u8 *uuid);
+
+#endif /* UUID_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/wpa_debug.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/wpa_debug.c
new file mode 100644
index 0000000..05f6aa8
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/wpa_debug.c
@@ -0,0 +1,1034 @@
+/*
+ * wpa_supplicant/hostapd / Debug prints
+ * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+
+#ifdef CONFIG_DEBUG_SYSLOG
+#include <syslog.h>
+#endif /* CONFIG_DEBUG_SYSLOG */
+
+#ifdef CONFIG_DEBUG_LINUX_TRACING
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdio.h>
+
+static FILE *wpa_debug_tracing_file = NULL;
+
+#define WPAS_TRACE_PFX "wpas <%d>: "
+#endif /* CONFIG_DEBUG_LINUX_TRACING */
+
+
+int wpa_debug_level = MSG_INFO;
+int wpa_debug_show_keys = 0;
+int wpa_debug_timestamp = 0;
+int wpa_debug_syslog = 0;
+#ifndef CONFIG_NO_STDOUT_DEBUG
+static FILE *out_file = NULL;
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+
+
+#ifdef CONFIG_ANDROID_LOG
+
+#include <android/log.h>
+
+#ifndef ANDROID_LOG_NAME
+#define ANDROID_LOG_NAME	"wpa_supplicant"
+#endif /* ANDROID_LOG_NAME */
+
+static int wpa_to_android_level(int level)
+{
+	if (level == MSG_ERROR)
+		return ANDROID_LOG_ERROR;
+	if (level == MSG_WARNING)
+		return ANDROID_LOG_WARN;
+	if (level == MSG_INFO)
+		return ANDROID_LOG_INFO;
+	return ANDROID_LOG_DEBUG;
+}
+
+#endif /* CONFIG_ANDROID_LOG */
+
+#ifndef CONFIG_NO_STDOUT_DEBUG
+
+#if 1
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+//#include <linux/time.h>
+//#include <linux/rtc.h>
+#include <time.h>
+#else
+#ifdef CONFIG_DEBUG_FILE
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#endif /* CONFIG_DEBUG_FILE */
+#endif
+
+
+void wpa_debug_print_timestamp(void)
+{
+#ifndef CONFIG_ANDROID_LOG
+	struct os_time tv;
+
+	if (!wpa_debug_timestamp)
+		return;
+
+	os_get_time(&tv);
+#ifdef CONFIG_DEBUG_FILE
+	if (out_file)
+		fprintf(out_file, "%ld.%06u: ", (long) tv.sec,
+			(unsigned int) tv.usec);
+#endif /* CONFIG_DEBUG_FILE */
+	if (!out_file && !wpa_debug_syslog)
+		printf("%ld.%06u: ", (long) tv.sec, (unsigned int) tv.usec);
+#endif /* CONFIG_ANDROID_LOG */
+}
+
+
+#ifdef CONFIG_DEBUG_SYSLOG
+#ifndef LOG_HOSTAPD
+#define LOG_HOSTAPD LOG_DAEMON
+#endif /* LOG_HOSTAPD */
+
+void wpa_debug_open_syslog(void)
+{
+	openlog("wpa_supplicant", LOG_PID | LOG_NDELAY, LOG_HOSTAPD);
+	wpa_debug_syslog++;
+}
+
+
+void wpa_debug_close_syslog(void)
+{
+	if (wpa_debug_syslog)
+		closelog();
+}
+
+
+static int syslog_priority(int level)
+{
+	switch (level) {
+	case MSG_MSGDUMP:
+	case MSG_DEBUG:
+		return LOG_DEBUG;
+	case MSG_INFO:
+		return LOG_NOTICE;
+	case MSG_WARNING:
+		return LOG_WARNING;
+	case MSG_ERROR:
+		return LOG_ERR;
+	}
+	return LOG_INFO;
+}
+#endif /* CONFIG_DEBUG_SYSLOG */
+
+
+#ifdef CONFIG_DEBUG_LINUX_TRACING
+
+int wpa_debug_open_linux_tracing(void)
+{
+	int mounts, trace_fd;
+	char buf[4096] = {};
+	ssize_t buflen;
+	char *line, *tmp1, *path = NULL;
+
+	mounts = open("/proc/mounts", O_RDONLY);
+	if (mounts < 0) {
+		printf("no /proc/mounts\n");
+		return -1;
+	}
+
+	buflen = read(mounts, buf, sizeof(buf) - 1);
+	close(mounts);
+	if (buflen < 0) {
+		printf("failed to read /proc/mounts\n");
+		return -1;
+	}
+	buf[buflen] = '\0';
+
+	line = strtok_r(buf, "\n", &tmp1);
+	while (line) {
+		char *tmp2, *tmp_path, *fstype;
+		/* "<dev> <mountpoint> <fs type> ..." */
+		strtok_r(line, " ", &tmp2);
+		tmp_path = strtok_r(NULL, " ", &tmp2);
+		fstype = strtok_r(NULL, " ", &tmp2);
+		if (fstype && strcmp(fstype, "debugfs") == 0) {
+			path = tmp_path;
+			break;
+		}
+
+		line = strtok_r(NULL, "\n", &tmp1);
+	}
+
+	if (path == NULL) {
+		printf("debugfs mountpoint not found\n");
+		return -1;
+	}
+
+	snprintf(buf, sizeof(buf) - 1, "%s/tracing/trace_marker", path);
+
+	trace_fd = open(buf, O_WRONLY);
+	if (trace_fd < 0) {
+		printf("failed to open trace_marker file\n");
+		return -1;
+	}
+	wpa_debug_tracing_file = fdopen(trace_fd, "w");
+	if (wpa_debug_tracing_file == NULL) {
+		close(trace_fd);
+		printf("failed to fdopen()\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+void wpa_debug_close_linux_tracing(void)
+{
+	if (wpa_debug_tracing_file == NULL)
+		return;
+	fclose(wpa_debug_tracing_file);
+	wpa_debug_tracing_file = NULL;
+}
+
+#endif /* CONFIG_DEBUG_LINUX_TRACING */
+
+static int wpa_debug_file_log = 1;  // Global variable to enable file printing
+static int wpa_debug_sys_level = MSG_INFO;  // Log level for acat printing
+static int wpa_debug_file_level = MSG_DEBUG;  // Log level for file printing
+
+#if 1
+static FILE *asr_out_file_1 = NULL;
+static FILE *asr_out_file_2 = NULL;
+
+static int curr_write_buf = 1;
+static int asr_hostapd_log_num = 0;
+
+//#define ASR_HOSTAPD_LOG1      "/etc/wpa_hpd_log/hostapd.log1"
+//#define ASR_HOSTAPD_LOG2      "/etc/wpa_hpd_log/hostapd.log2"
+
+#define HOSTAPD_LOG_LINE_MAX  30000
+#define ASR_HOSTAPD_LOG1      "/tmp/hostapd.log1"
+#define ASR_HOSTAPD_LOG2      "/tmp/hostapd.log2"
+
+
+int asr_wpa_debug_open_file(const char *path, int bufidx)
+{
+	int out_fd;
+	FILE *asr_out_file;
+
+	if (!path)
+		return 0;
+
+	out_fd = open(path, O_CREAT | O_APPEND | O_WRONLY,
+		      S_IRUSR | S_IWUSR | S_IRGRP);
+	if (out_fd < 0) {
+		wpa_printf(MSG_ERROR,
+			   "%s: Failed to open output file descriptor, using standard output",
+			   __func__);
+		return -1;
+	}
+
+	if (fcntl(out_fd, F_SETFD, FD_CLOEXEC) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "%s: Failed to set FD_CLOEXEC - continue without: %s",
+			   __func__, strerror(errno));
+	}
+
+	asr_out_file = fdopen(out_fd, "w");
+	if (asr_out_file == NULL) {
+		wpa_printf(MSG_ERROR, "wpa_debug_open_file: Failed to open "
+			   "output file, using standard output");
+		close(out_fd);
+		return -1;
+	}
+
+	setvbuf(asr_out_file, NULL, _IOLBF, 0);
+
+	if (1 == bufidx)
+		asr_out_file_1 = asr_out_file;
+	else
+		asr_out_file_2 = asr_out_file;
+
+	return 0;
+}
+
+
+void asr_wpa_debug_close_file(int bufidx)
+{
+	if (1 == bufidx) {
+		if (!asr_out_file_1)
+			return;
+
+		fclose(asr_out_file_1);
+		asr_out_file_1 = NULL;
+		remove(ASR_HOSTAPD_LOG1);
+	}
+	else if (2 == bufidx) {
+		if (!asr_out_file_2)
+			return;
+
+		fclose(asr_out_file_2);
+		asr_out_file_2 = NULL;
+		remove(ASR_HOSTAPD_LOG2);
+	}
+}
+
+#endif
+
+/**
+ * wpa_printf - conditional printf
+ * @level: priority level (MSG_*) of the message
+ * @fmt: printf format string, followed by optional arguments
+ *
+ * This function is used to print conditional debugging and error messages. The
+ * output may be directed to stdout, stderr, and/or syslog based on
+ * configuration.
+ *
+ * Note: New line '\n' is added to the end of the text when printing to stdout.
+ */
+void _wpa_printf(int level, const char *fmt, ...)
+{
+	va_list ap;
+		
+#ifdef CONFIG_ANDROID_LOG
+	if (level >= wpa_debug_level) {	
+		va_start(ap, fmt);
+		__android_log_vprint(wpa_to_android_level(level),
+				     ANDROID_LOG_NAME, fmt, ap);
+		va_end(ap);
+	}
+#else /* CONFIG_ANDROID_LOG */
+
+#ifdef CONFIG_DEBUG_SYSLOG
+	if (level >= wpa_debug_sys_level) {
+		if (wpa_debug_syslog) {
+			va_start(ap, fmt);
+			vsyslog(syslog_priority(level), fmt, ap);
+			va_end(ap);
+		}
+	}
+#endif /* CONFIG_DEBUG_SYSLOG */
+
+	wpa_debug_print_timestamp();
+#ifdef CONFIG_DEBUG_FILE
+		if (out_file) {
+			va_start(ap, fmt);
+			vfprintf(out_file, fmt, ap);
+			fprintf(out_file, "\n");
+			va_end(ap);
+		}
+#endif /* CONFIG_DEBUG_FILE */
+
+	if (wpa_debug_file_log && (level >= wpa_debug_file_level)) {
+#if 1
+		time_t real_time;
+		struct tm *Now;
+		//struct os_time tv;
+		//os_get_time(&tv);   
+
+		time(&real_time);
+		Now = localtime(&real_time);      
+	      
+		if (1 == curr_write_buf) {
+			if (NULL == asr_out_file_1) {
+				asr_wpa_debug_open_file(ASR_HOSTAPD_LOG1, 1);
+			}
+
+			if (asr_out_file_1) {
+				//fprintf(asr_out_file_1, "%ld.%06u: ", (long) tv.sec,
+				//  (unsigned int) tv.usec);                  
+          		fprintf(asr_out_file_1, "%s", asctime(Now));       
+				//fprintf(asr_out_file_1, "%d-%d-%d: ", tm.tm_hour+8,
+				//  tm.tm_min, tm.tm_sec);    
+				va_start(ap, fmt);
+				vfprintf(asr_out_file_1, fmt, ap);
+				fprintf(asr_out_file_1, "\n");
+				va_end(ap);
+			}
+
+			asr_hostapd_log_num++;
+			if (asr_hostapd_log_num >= HOSTAPD_LOG_LINE_MAX) {
+				asr_hostapd_log_num = 0;
+				asr_wpa_debug_close_file(2);
+				curr_write_buf = 2;
+			}
+		}
+
+		if (2 == curr_write_buf) {
+			if (NULL == asr_out_file_2) {
+				asr_wpa_debug_open_file(ASR_HOSTAPD_LOG2, 2);
+			}
+
+			if (asr_out_file_2) {
+				//fprintf(asr_out_file_2, "%ld.%06u: ", (long) tv.sec,
+				//  (unsigned int) tv.usec);                    
+          		fprintf(asr_out_file_2, "%s", asctime(Now));
+				va_start(ap, fmt);
+				vfprintf(asr_out_file_2, fmt, ap);
+				fprintf(asr_out_file_2, "\n");
+				va_end(ap);
+			}
+
+			asr_hostapd_log_num++;
+			if (asr_hostapd_log_num >= HOSTAPD_LOG_LINE_MAX) {
+				asr_hostapd_log_num = 0;
+				asr_wpa_debug_close_file(1);
+				curr_write_buf = 1;
+			}
+		}		
+#else
+		if (!wpa_debug_syslog && !out_file) {
+			va_start(ap, fmt);
+			vprintf(fmt, ap);
+			printf("\n");
+			va_end(ap);
+		}
+#endif
+	}
+#endif /* CONFIG_ANDROID_LOG */
+
+#ifdef CONFIG_DEBUG_LINUX_TRACING
+	if ((!wpa_debug_file_log) && (wpa_debug_tracing_file != NULL)) {
+		va_start(ap, fmt);
+		fprintf(wpa_debug_tracing_file, WPAS_TRACE_PFX, level);
+		vfprintf(wpa_debug_tracing_file, fmt, ap);
+		fprintf(wpa_debug_tracing_file, "\n");
+		fflush(wpa_debug_tracing_file);
+		va_end(ap);
+	}
+#endif /* CONFIG_DEBUG_LINUX_TRACING */
+}
+
+
+void _wpa_hexdump(int level, const char *title, const u8 *buf,
+			 size_t len, int show, int only_syslog)
+{
+	size_t i;
+
+#ifdef CONFIG_DEBUG_LINUX_TRACING
+	if (wpa_debug_tracing_file != NULL) {
+		fprintf(wpa_debug_tracing_file,
+			WPAS_TRACE_PFX "%s - hexdump(len=%lu):",
+			level, title, (unsigned long) len);
+		if (buf == NULL) {
+			fprintf(wpa_debug_tracing_file, " [NULL]\n");
+		} else if (!show) {
+			fprintf(wpa_debug_tracing_file, " [REMOVED]\n");
+		} else {
+			for (i = 0; i < len; i++)
+				fprintf(wpa_debug_tracing_file,
+					" %02x", buf[i]);
+		}
+		fflush(wpa_debug_tracing_file);
+	}
+#endif /* CONFIG_DEBUG_LINUX_TRACING */
+
+	if (level < wpa_debug_level)
+		return;
+#ifdef CONFIG_ANDROID_LOG
+	{
+		const char *display;
+		char *strbuf = NULL;
+		size_t slen = len;
+		if (buf == NULL) {
+			display = " [NULL]";
+		} else if (len == 0) {
+			display = "";
+		} else if (show && len) {
+			/* Limit debug message length for Android log */
+			if (slen > 32)
+				slen = 32;
+			strbuf = os_malloc(1 + 3 * slen);
+			if (strbuf == NULL) {
+				wpa_printf(MSG_ERROR, "wpa_hexdump: Failed to "
+					   "allocate message buffer");
+				return;
+			}
+
+			for (i = 0; i < slen; i++)
+				os_snprintf(&strbuf[i * 3], 4, " %02x",
+					    buf[i]);
+
+			display = strbuf;
+		} else {
+			display = " [REMOVED]";
+		}
+
+		__android_log_print(wpa_to_android_level(level),
+				    ANDROID_LOG_NAME,
+				    "%s - hexdump(len=%lu):%s%s",
+				    title, (long unsigned int) len, display,
+				    len > slen ? " ..." : "");
+		bin_clear_free(strbuf, 1 + 3 * slen);
+		return;
+	}
+#else /* CONFIG_ANDROID_LOG */
+#if 0
+#ifdef CONFIG_DEBUG_SYSLOG
+	if (wpa_debug_syslog) {
+		const char *display;
+		char *strbuf = NULL;
+
+		if (buf == NULL) {
+			display = " [NULL]";
+		} else if (len == 0) {
+			display = "";
+		} else if (show && len) {
+			strbuf = os_malloc(1 + 3 * len);
+			if (strbuf == NULL) {
+				wpa_printf(MSG_ERROR, "wpa_hexdump: Failed to "
+					   "allocate message buffer");
+				return;
+			}
+
+			for (i = 0; i < len; i++)
+				os_snprintf(&strbuf[i * 3], 4, " %02x",
+					    buf[i]);
+
+			display = strbuf;
+		} else {
+			display = " [REMOVED]";
+		}
+
+		syslog(syslog_priority(level), "%s - hexdump(len=%lu):%s",
+		       title, (unsigned long) len, display);
+		bin_clear_free(strbuf, 1 + 3 * len);
+		if (only_syslog)
+			return;
+	}
+#endif /* CONFIG_DEBUG_SYSLOG */
+#endif
+	wpa_debug_print_timestamp();
+#ifdef CONFIG_DEBUG_FILE
+	if (out_file) {
+		fprintf(out_file, "%s - hexdump(len=%lu):",
+			title, (unsigned long) len);
+		if (buf == NULL) {
+			fprintf(out_file, " [NULL]");
+		} else if (show) {
+			for (i = 0; i < len; i++)
+				fprintf(out_file, " %02x", buf[i]);
+		} else {
+			fprintf(out_file, " [REMOVED]");
+		}
+		fprintf(out_file, "\n");
+	}
+#endif /* CONFIG_DEBUG_FILE */
+	if (!wpa_debug_syslog && !out_file) {
+		printf("%s - hexdump(len=%lu):", title, (unsigned long) len);
+		if (buf == NULL) {
+			printf(" [NULL]");
+		} else if (show) {
+			for (i = 0; i < len; i++)
+				printf(" %02x", buf[i]);
+		} else {
+			printf(" [REMOVED]");
+		}
+		printf("\n");
+	}
+#endif /* CONFIG_ANDROID_LOG */
+}
+
+void _wpa_hexdump_ascii(int level, const char *title, const void *buf,
+			       size_t len, int show)
+{
+	size_t i, llen;
+	const u8 *pos = buf;
+	const size_t line_len = 16;
+
+#ifdef CONFIG_DEBUG_LINUX_TRACING
+	if (wpa_debug_tracing_file != NULL) {
+		fprintf(wpa_debug_tracing_file,
+			WPAS_TRACE_PFX "%s - hexdump_ascii(len=%lu):",
+			level, title, (unsigned long) len);
+		if (buf == NULL) {
+			fprintf(wpa_debug_tracing_file, " [NULL]\n");
+		} else if (!show) {
+			fprintf(wpa_debug_tracing_file, " [REMOVED]\n");
+		} else {
+			/* can do ascii processing in userspace */
+			for (i = 0; i < len; i++)
+				fprintf(wpa_debug_tracing_file,
+					" %02x", pos[i]);
+		}
+		fflush(wpa_debug_tracing_file);
+	}
+#endif /* CONFIG_DEBUG_LINUX_TRACING */
+
+	if (level < wpa_debug_level)
+		return;
+#ifdef CONFIG_ANDROID_LOG
+	_wpa_hexdump(level, title, buf, len, show, 0);
+#else /* CONFIG_ANDROID_LOG */
+#ifdef CONFIG_DEBUG_SYSLOG
+	if (wpa_debug_syslog)
+		_wpa_hexdump(level, title, buf, len, show, 1);
+#endif /* CONFIG_DEBUG_SYSLOG */
+	wpa_debug_print_timestamp();
+#ifdef CONFIG_DEBUG_FILE
+	if (out_file) {
+		if (!show) {
+			fprintf(out_file,
+				"%s - hexdump_ascii(len=%lu): [REMOVED]\n",
+				title, (unsigned long) len);
+			goto file_done;
+		}
+		if (buf == NULL) {
+			fprintf(out_file,
+				"%s - hexdump_ascii(len=%lu): [NULL]\n",
+				title, (unsigned long) len);
+			goto file_done;
+		}
+		fprintf(out_file, "%s - hexdump_ascii(len=%lu):\n",
+			title, (unsigned long) len);
+		while (len) {
+			llen = len > line_len ? line_len : len;
+			fprintf(out_file, "    ");
+			for (i = 0; i < llen; i++)
+				fprintf(out_file, " %02x", pos[i]);
+			for (i = llen; i < line_len; i++)
+				fprintf(out_file, "   ");
+			fprintf(out_file, "   ");
+			for (i = 0; i < llen; i++) {
+				if (isprint(pos[i]))
+					fprintf(out_file, "%c", pos[i]);
+				else
+					fprintf(out_file, "_");
+			}
+			for (i = llen; i < line_len; i++)
+				fprintf(out_file, " ");
+			fprintf(out_file, "\n");
+			pos += llen;
+			len -= llen;
+		}
+	}
+file_done:
+#endif /* CONFIG_DEBUG_FILE */
+	if (!wpa_debug_syslog && !out_file) {
+		if (!show) {
+			printf("%s - hexdump_ascii(len=%lu): [REMOVED]\n",
+			       title, (unsigned long) len);
+			return;
+		}
+		if (buf == NULL) {
+			printf("%s - hexdump_ascii(len=%lu): [NULL]\n",
+			       title, (unsigned long) len);
+			return;
+		}
+		printf("%s - hexdump_ascii(len=%lu):\n", title,
+		       (unsigned long) len);
+		while (len) {
+			llen = len > line_len ? line_len : len;
+			printf("    ");
+			for (i = 0; i < llen; i++)
+				printf(" %02x", pos[i]);
+			for (i = llen; i < line_len; i++)
+				printf("   ");
+			printf("   ");
+			for (i = 0; i < llen; i++) {
+				if (isprint(pos[i]))
+					printf("%c", pos[i]);
+				else
+					printf("_");
+			}
+			for (i = llen; i < line_len; i++)
+				printf(" ");
+			printf("\n");
+			pos += llen;
+			len -= llen;
+		}
+	}
+#endif /* CONFIG_ANDROID_LOG */
+}
+
+
+#ifdef CONFIG_DEBUG_FILE
+static char *last_path = NULL;
+#endif /* CONFIG_DEBUG_FILE */
+
+int wpa_debug_reopen_file(void)
+{
+#ifdef CONFIG_DEBUG_FILE
+	int rv;
+	char *tmp;
+
+	if (!last_path)
+		return 0; /* logfile not used */
+
+	tmp = os_strdup(last_path);
+	if (!tmp)
+		return -1;
+
+	wpa_debug_close_file();
+	rv = wpa_debug_open_file(tmp);
+	os_free(tmp);
+	return rv;
+#else /* CONFIG_DEBUG_FILE */
+	return 0;
+#endif /* CONFIG_DEBUG_FILE */
+}
+
+
+int wpa_debug_open_file(const char *path)
+{
+#ifdef CONFIG_DEBUG_FILE
+	int out_fd;
+
+	if (!path)
+		return 0;
+
+	if (last_path == NULL || os_strcmp(last_path, path) != 0) {
+		/* Save our path to enable re-open */
+		os_free(last_path);
+		last_path = os_strdup(path);
+	}
+
+	out_fd = open(path, O_CREAT | O_APPEND | O_WRONLY,
+		      S_IRUSR | S_IWUSR | S_IRGRP);
+	if (out_fd < 0) {
+		wpa_printf(MSG_ERROR,
+			   "%s: Failed to open output file descriptor, using standard output",
+			   __func__);
+		return -1;
+	}
+
+#ifdef __linux__
+	if (fcntl(out_fd, F_SETFD, FD_CLOEXEC) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "%s: Failed to set FD_CLOEXEC - continue without: %s",
+			   __func__, strerror(errno));
+	}
+#endif /* __linux__ */
+
+	out_file = fdopen(out_fd, "a");
+	if (out_file == NULL) {
+		wpa_printf(MSG_ERROR, "wpa_debug_open_file: Failed to open "
+			   "output file, using standard output");
+		close(out_fd);
+		return -1;
+	}
+#ifndef _WIN32
+	setvbuf(out_file, NULL, _IOLBF, 0);
+#endif /* _WIN32 */
+#else /* CONFIG_DEBUG_FILE */
+	(void)path;
+#endif /* CONFIG_DEBUG_FILE */
+	return 0;
+}
+
+
+void wpa_debug_stop_log(void)
+{
+#ifdef CONFIG_DEBUG_FILE
+	if (!out_file)
+		return;
+	fclose(out_file);
+	out_file = NULL;
+#endif /* CONFIG_DEBUG_FILE */
+}
+
+
+void wpa_debug_close_file(void)
+{
+#ifdef CONFIG_DEBUG_FILE
+	wpa_debug_stop_log();
+	os_free(last_path);
+	last_path = NULL;
+#endif /* CONFIG_DEBUG_FILE */
+}
+
+
+void wpa_debug_setup_stdout(void)
+{
+#ifndef _WIN32
+	setvbuf(stdout, NULL, _IOLBF, 0);
+#endif /* _WIN32 */
+}
+
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+
+
+#ifndef CONFIG_NO_WPA_MSG
+static wpa_msg_cb_func wpa_msg_cb = NULL;
+
+void wpa_msg_register_cb(wpa_msg_cb_func func)
+{
+	wpa_msg_cb = func;
+}
+
+
+static wpa_msg_get_ifname_func wpa_msg_ifname_cb = NULL;
+
+void wpa_msg_register_ifname_cb(wpa_msg_get_ifname_func func)
+{
+	wpa_msg_ifname_cb = func;
+}
+
+
+void _wpa_msg(void *ctx, int level, const char *fmt, ...)
+{
+	va_list ap;
+	char *buf;
+	int buflen;
+	int len;
+	char prefix[130];
+
+	va_start(ap, fmt);
+	buflen = vsnprintf(NULL, 0, fmt, ap) + 1;
+	va_end(ap);
+
+	buf = os_malloc(buflen);
+	if (buf == NULL) {
+		wpa_printf(MSG_ERROR, "wpa_msg: Failed to allocate message "
+			   "buffer");
+		return;
+	}
+	va_start(ap, fmt);
+	prefix[0] = '\0';
+	if (wpa_msg_ifname_cb) {
+		const char *ifname = wpa_msg_ifname_cb(ctx);
+		if (ifname) {
+			int res = os_snprintf(prefix, sizeof(prefix), "%s: ",
+					      ifname);
+			if (os_snprintf_error(sizeof(prefix), res))
+				prefix[0] = '\0';
+		}
+	}
+	len = vsnprintf(buf, buflen, fmt, ap);
+	va_end(ap);
+	wpa_printf(level, "%s%s", prefix, buf);
+	if (wpa_msg_cb)
+		wpa_msg_cb(ctx, level, WPA_MSG_PER_INTERFACE, buf, len);
+	bin_clear_free(buf, buflen);
+}
+
+
+void _wpa_msg_ctrl(void *ctx, int level, const char *fmt, ...)
+{
+	va_list ap;
+	char *buf;
+	int buflen;
+	int len;
+
+	if (!wpa_msg_cb)
+		return;
+
+	va_start(ap, fmt);
+	buflen = vsnprintf(NULL, 0, fmt, ap) + 1;
+	va_end(ap);
+
+	buf = os_malloc(buflen);
+	if (buf == NULL) {
+		wpa_printf(MSG_ERROR, "wpa_msg_ctrl: Failed to allocate "
+			   "message buffer");
+		return;
+	}
+	va_start(ap, fmt);
+	len = vsnprintf(buf, buflen, fmt, ap);
+	va_end(ap);
+	wpa_msg_cb(ctx, level, WPA_MSG_PER_INTERFACE, buf, len);
+	bin_clear_free(buf, buflen);
+}
+
+
+void wpa_msg_global(void *ctx, int level, const char *fmt, ...)
+{
+	va_list ap;
+	char *buf;
+	int buflen;
+	int len;
+
+	va_start(ap, fmt);
+	buflen = vsnprintf(NULL, 0, fmt, ap) + 1;
+	va_end(ap);
+
+	buf = os_malloc(buflen);
+	if (buf == NULL) {
+		wpa_printf(MSG_ERROR, "wpa_msg_global: Failed to allocate "
+			   "message buffer");
+		return;
+	}
+	va_start(ap, fmt);
+	len = vsnprintf(buf, buflen, fmt, ap);
+	va_end(ap);
+	wpa_printf(level, "%s", buf);
+	if (wpa_msg_cb)
+		wpa_msg_cb(ctx, level, WPA_MSG_GLOBAL, buf, len);
+	bin_clear_free(buf, buflen);
+}
+
+
+void wpa_msg_global_ctrl(void *ctx, int level, const char *fmt, ...)
+{
+	va_list ap;
+	char *buf;
+	int buflen;
+	int len;
+
+	if (!wpa_msg_cb)
+		return;
+
+	va_start(ap, fmt);
+	buflen = vsnprintf(NULL, 0, fmt, ap) + 1;
+	va_end(ap);
+
+	buf = os_malloc(buflen);
+	if (buf == NULL) {
+		wpa_printf(MSG_ERROR,
+			   "wpa_msg_global_ctrl: Failed to allocate message buffer");
+		return;
+	}
+	va_start(ap, fmt);
+	len = vsnprintf(buf, buflen, fmt, ap);
+	va_end(ap);
+	wpa_msg_cb(ctx, level, WPA_MSG_GLOBAL, buf, len);
+	bin_clear_free(buf, buflen);
+}
+
+
+void wpa_msg_no_global(void *ctx, int level, const char *fmt, ...)
+{
+	va_list ap;
+	char *buf;
+	int buflen;
+	int len;
+
+	va_start(ap, fmt);
+	buflen = vsnprintf(NULL, 0, fmt, ap) + 1;
+	va_end(ap);
+
+	buf = os_malloc(buflen);
+	if (buf == NULL) {
+		wpa_printf(MSG_ERROR, "wpa_msg_no_global: Failed to allocate "
+			   "message buffer");
+		return;
+	}
+	va_start(ap, fmt);
+	len = vsnprintf(buf, buflen, fmt, ap);
+	va_end(ap);
+	wpa_printf(level, "%s", buf);
+	if (wpa_msg_cb)
+		wpa_msg_cb(ctx, level, WPA_MSG_NO_GLOBAL, buf, len);
+	bin_clear_free(buf, buflen);
+}
+
+
+void wpa_msg_global_only(void *ctx, int level, const char *fmt, ...)
+{
+	va_list ap;
+	char *buf;
+	int buflen;
+	int len;
+
+	va_start(ap, fmt);
+	buflen = vsnprintf(NULL, 0, fmt, ap) + 1;
+	va_end(ap);
+
+	buf = os_malloc(buflen);
+	if (buf == NULL) {
+		wpa_printf(MSG_ERROR, "%s: Failed to allocate message buffer",
+			   __func__);
+		return;
+	}
+	va_start(ap, fmt);
+	len = vsnprintf(buf, buflen, fmt, ap);
+	va_end(ap);
+	wpa_printf(level, "%s", buf);
+	if (wpa_msg_cb)
+		wpa_msg_cb(ctx, level, WPA_MSG_ONLY_GLOBAL, buf, len);
+	os_free(buf);
+}
+
+#endif /* CONFIG_NO_WPA_MSG */
+
+
+#ifndef CONFIG_NO_HOSTAPD_LOGGER
+static hostapd_logger_cb_func hostapd_logger_cb = NULL;
+
+void hostapd_logger_register_cb(hostapd_logger_cb_func func)
+{
+	hostapd_logger_cb = func;
+}
+
+
+void hostapd_logger(void *ctx, const u8 *addr, unsigned int module, int level,
+		    const char *fmt, ...)
+{
+	va_list ap;
+	char *buf;
+	int buflen;
+	int len;
+
+	va_start(ap, fmt);
+	buflen = vsnprintf(NULL, 0, fmt, ap) + 1;
+	va_end(ap);
+
+	buf = os_malloc(buflen);
+	if (buf == NULL) {
+		wpa_printf(MSG_ERROR, "hostapd_logger: Failed to allocate "
+			   "message buffer");
+		return;
+	}
+	va_start(ap, fmt);
+	len = vsnprintf(buf, buflen, fmt, ap);
+	va_end(ap);
+	if (hostapd_logger_cb)
+		hostapd_logger_cb(ctx, addr, module, level, buf, len);
+	else if (addr)
+		wpa_printf(MSG_DEBUG, "hostapd_logger: STA " MACSTR " - %s",
+			   MAC2STR(addr), buf);
+	else
+		wpa_printf(MSG_DEBUG, "hostapd_logger: %s", buf);
+	bin_clear_free(buf, buflen);
+}
+#endif /* CONFIG_NO_HOSTAPD_LOGGER */
+
+
+const char * debug_level_str(int level)
+{
+	switch (level) {
+	case MSG_EXCESSIVE:
+		return "EXCESSIVE";
+	case MSG_MSGDUMP:
+		return "MSGDUMP";
+	case MSG_DEBUG:
+		return "DEBUG";
+	case MSG_INFO:
+		return "INFO";
+	case MSG_WARNING:
+		return "WARNING";
+	case MSG_ERROR:
+		return "ERROR";
+	default:
+		return "?";
+	}
+}
+
+
+int str_to_debug_level(const char *s)
+{
+	if (os_strcasecmp(s, "EXCESSIVE") == 0)
+		return MSG_EXCESSIVE;
+	if (os_strcasecmp(s, "MSGDUMP") == 0)
+		return MSG_MSGDUMP;
+	if (os_strcasecmp(s, "DEBUG") == 0)
+		return MSG_DEBUG;
+	if (os_strcasecmp(s, "INFO") == 0)
+		return MSG_INFO;
+	if (os_strcasecmp(s, "WARNING") == 0)
+		return MSG_WARNING;
+	if (os_strcasecmp(s, "ERROR") == 0)
+		return MSG_ERROR;
+	return -1;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/wpa_debug.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/wpa_debug.h
new file mode 100644
index 0000000..a57f1d5
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/wpa_debug.h
@@ -0,0 +1,422 @@
+/*
+ * wpa_supplicant/hostapd / Debug prints
+ * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WPA_DEBUG_H
+#define WPA_DEBUG_H
+
+#include "wpabuf.h"
+
+extern int wpa_debug_level;
+extern int wpa_debug_show_keys;
+extern int wpa_debug_timestamp;
+extern int wpa_debug_syslog;
+
+/* Debugging function - conditional printf and hex dump. Driver wrappers can
+ * use these for debugging purposes. */
+
+enum {
+	MSG_EXCESSIVE, MSG_MSGDUMP, MSG_DEBUG, MSG_INFO, MSG_WARNING, MSG_ERROR
+};
+
+#ifdef CONFIG_NO_STDOUT_DEBUG
+
+#define wpa_debug_print_timestamp() do { } while (0)
+#define wpa_printf(args...) do { } while (0)
+#define wpa_hexdump(l,t,b,le) do { } while (0)
+#define wpa_hexdump_buf(l,t,b) do { } while (0)
+#define wpa_hexdump_key(l,t,b,le) do { } while (0)
+#define wpa_hexdump_buf_key(l,t,b) do { } while (0)
+#define wpa_hexdump_ascii(l,t,b,le) do { } while (0)
+#define wpa_hexdump_ascii_key(l,t,b,le) do { } while (0)
+#define wpa_debug_open_file(p) do { } while (0)
+#define wpa_debug_close_file() do { } while (0)
+#define wpa_debug_setup_stdout() do { } while (0)
+#define wpa_dbg(args...) do { } while (0)
+
+static inline int wpa_debug_reopen_file(void)
+{
+	return 0;
+}
+
+#else /* CONFIG_NO_STDOUT_DEBUG */
+
+int wpa_debug_open_file(const char *path);
+int wpa_debug_reopen_file(void);
+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
+ *
+ * This function prints a timestamp in seconds_from_1970.microsoconds
+ * format if debug output has been configured to include timestamps in debug
+ * messages.
+ */
+void wpa_debug_print_timestamp(void);
+
+/**
+ * wpa_printf - conditional printf
+ * @level: priority level (MSG_*) of the message
+ * @fmt: printf format string, followed by optional arguments
+ *
+ * This function is used to print conditional debugging and error messages. The
+ * output may be directed to stdout, stderr, and/or syslog based on
+ * configuration.
+ *
+ * Note: New line '\n' is added to the end of the text when printing to stdout.
+ */
+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
+ * @title: title of for the message
+ * @buf: data buffer to be dumped
+ * @len: length of the buf
+ *
+ * This function is used to print conditional debugging and error messages. The
+ * output may be directed to stdout, stderr, and/or syslog based on
+ * configuration. The contents of buf is printed out has hex dump.
+ */
+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)
+{
+	wpa_hexdump(level, title, buf ? wpabuf_head(buf) : NULL,
+		    buf ? wpabuf_len(buf) : 0);
+}
+
+/**
+ * wpa_hexdump_key - conditional hex dump, hide keys
+ * @level: priority level (MSG_*) of the message
+ * @title: title of for the message
+ * @buf: data buffer to be dumped
+ * @len: length of the buf
+ *
+ * This function is used to print conditional debugging and error messages. The
+ * output may be directed to stdout, stderr, and/or syslog based on
+ * configuration. The contents of buf is printed out has hex dump. This works
+ * like wpa_hexdump(), but by default, does not include secret keys (passwords,
+ * etc.) in debug output.
+ */
+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)
+{
+	wpa_hexdump_key(level, title, buf ? wpabuf_head(buf) : NULL,
+			buf ? wpabuf_len(buf) : 0);
+}
+
+/**
+ * wpa_hexdump_ascii - conditional hex dump
+ * @level: priority level (MSG_*) of the message
+ * @title: title of for the message
+ * @buf: data buffer to be dumped
+ * @len: length of the buf
+ *
+ * This function is used to print conditional debugging and error messages. The
+ * output may be directed to stdout, stderr, and/or syslog based on
+ * configuration. The contents of buf is printed out has hex dump with both
+ * the hex numbers and ASCII characters (for printable range) are shown. 16
+ * bytes per line will be shown.
+ */
+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
+ * @level: priority level (MSG_*) of the message
+ * @title: title of for the message
+ * @buf: data buffer to be dumped
+ * @len: length of the buf
+ *
+ * This function is used to print conditional debugging and error messages. The
+ * output may be directed to stdout, stderr, and/or syslog based on
+ * configuration. The contents of buf is printed out has hex dump with both
+ * the hex numbers and ASCII characters (for printable range) are shown. 16
+ * 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.
+ */
+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
+ * binary size. As such, it should be used with debugging messages that are not
+ * needed in the control interface while wpa_msg() has to be used for anything
+ * that needs to shown to control interface monitors.
+ */
+#define wpa_dbg(args...) wpa_msg(args)
+
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+
+
+#ifdef CONFIG_NO_WPA_MSG
+#define wpa_msg(args...) do { } while (0)
+#define wpa_msg_ctrl(args...) do { } while (0)
+#define wpa_msg_global(args...) do { } while (0)
+#define wpa_msg_global_ctrl(args...) do { } while (0)
+#define wpa_msg_no_global(args...) do { } while (0)
+#define wpa_msg_global_only(args...) do { } while (0)
+#define wpa_msg_register_cb(f) do { } while (0)
+#define wpa_msg_register_ifname_cb(f) do { } while (0)
+#else /* CONFIG_NO_WPA_MSG */
+/**
+ * wpa_msg - Conditional printf for default target and ctrl_iface monitors
+ * @ctx: Pointer to context data; this is the ctx variable registered
+ *	with struct wpa_driver_ops::init()
+ * @level: priority level (MSG_*) of the message
+ * @fmt: printf format string, followed by optional arguments
+ *
+ * This function is used to print conditional debugging and error messages. The
+ * output may be directed to stdout, stderr, and/or syslog based on
+ * configuration. This function is like wpa_printf(), but it also sends the
+ * same message to all attached ctrl_iface monitors.
+ *
+ * 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);
+#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
+ * @ctx: Pointer to context data; this is the ctx variable registered
+ *	with struct wpa_driver_ops::init()
+ * @level: priority level (MSG_*) of the message
+ * @fmt: printf format string, followed by optional arguments
+ *
+ * This function is used to print conditional debugging and error messages.
+ * This function is like wpa_msg(), but it sends the output only to the
+ * 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, ...)
+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
+ * @ctx: Pointer to context data; this is the ctx variable registered
+ *	with struct wpa_driver_ops::init()
+ * @level: priority level (MSG_*) of the message
+ * @fmt: printf format string, followed by optional arguments
+ *
+ * This function is used to print conditional debugging and error messages.
+ * This function is like wpa_msg(), but it sends the output as a global event,
+ * i.e., without being specific to an interface. For backwards compatibility,
+ * an old style event is also delivered on one of the interfaces (the one
+ * specified by the context data).
+ */
+void wpa_msg_global(void *ctx, int level, const char *fmt, ...)
+PRINTF_FORMAT(3, 4);
+
+/**
+ * wpa_msg_global_ctrl - Conditional global printf for ctrl_iface monitors
+ * @ctx: Pointer to context data; this is the ctx variable registered
+ *	with struct wpa_driver_ops::init()
+ * @level: priority level (MSG_*) of the message
+ * @fmt: printf format string, followed by optional arguments
+ *
+ * This function is used to print conditional debugging and error messages.
+ * This function is like wpa_msg_global(), but it sends the output only to the
+ * attached global 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_global_ctrl(void *ctx, int level, const char *fmt, ...)
+PRINTF_FORMAT(3, 4);
+
+/**
+ * wpa_msg_no_global - Conditional printf for ctrl_iface monitors
+ * @ctx: Pointer to context data; this is the ctx variable registered
+ *	with struct wpa_driver_ops::init()
+ * @level: priority level (MSG_*) of the message
+ * @fmt: printf format string, followed by optional arguments
+ *
+ * This function is used to print conditional debugging and error messages.
+ * This function is like wpa_msg(), but it does not send the output as a global
+ * event.
+ */
+void wpa_msg_no_global(void *ctx, int level, const char *fmt, ...)
+PRINTF_FORMAT(3, 4);
+
+/**
+ * wpa_msg_global_only - Conditional printf for ctrl_iface monitors
+ * @ctx: Pointer to context data; this is the ctx variable registered
+ *	with struct wpa_driver_ops::init()
+ * @level: priority level (MSG_*) of the message
+ * @fmt: printf format string, followed by optional arguments
+ *
+ * This function is used to print conditional debugging and error messages.
+ * This function is like wpa_msg_global(), but it sends the output only as a
+ * global event.
+ */
+void wpa_msg_global_only(void *ctx, int level, const char *fmt, ...)
+PRINTF_FORMAT(3, 4);
+
+enum wpa_msg_type {
+	WPA_MSG_PER_INTERFACE,
+	WPA_MSG_GLOBAL,
+	WPA_MSG_NO_GLOBAL,
+	WPA_MSG_ONLY_GLOBAL,
+};
+
+typedef void (*wpa_msg_cb_func)(void *ctx, int level, enum wpa_msg_type type,
+				const char *txt, size_t len);
+
+/**
+ * wpa_msg_register_cb - Register callback function for wpa_msg() messages
+ * @func: Callback function (%NULL to unregister)
+ */
+void wpa_msg_register_cb(wpa_msg_cb_func func);
+
+typedef const char * (*wpa_msg_get_ifname_func)(void *ctx);
+void wpa_msg_register_ifname_cb(wpa_msg_get_ifname_func func);
+
+#endif /* CONFIG_NO_WPA_MSG */
+
+#ifdef CONFIG_NO_HOSTAPD_LOGGER
+#define hostapd_logger(args...) do { } while (0)
+#define hostapd_logger_register_cb(f) do { } while (0)
+#else /* CONFIG_NO_HOSTAPD_LOGGER */
+void hostapd_logger(void *ctx, const u8 *addr, unsigned int module, int level,
+		    const char *fmt, ...) PRINTF_FORMAT(5, 6);
+
+typedef void (*hostapd_logger_cb_func)(void *ctx, const u8 *addr,
+				       unsigned int module, int level,
+				       const char *txt, size_t len);
+
+/**
+ * hostapd_logger_register_cb - Register callback function for hostapd_logger()
+ * @func: Callback function (%NULL to unregister)
+ */
+void hostapd_logger_register_cb(hostapd_logger_cb_func func);
+#endif /* CONFIG_NO_HOSTAPD_LOGGER */
+
+#define HOSTAPD_MODULE_IEEE80211	0x00000001
+#define HOSTAPD_MODULE_IEEE8021X	0x00000002
+#define HOSTAPD_MODULE_RADIUS		0x00000004
+#define HOSTAPD_MODULE_WPA		0x00000008
+#define HOSTAPD_MODULE_DRIVER		0x00000010
+#define HOSTAPD_MODULE_MLME		0x00000040
+
+enum hostapd_logger_level {
+	HOSTAPD_LEVEL_DEBUG_VERBOSE = 0,
+	HOSTAPD_LEVEL_DEBUG = 1,
+	HOSTAPD_LEVEL_INFO = 2,
+	HOSTAPD_LEVEL_NOTICE = 3,
+	HOSTAPD_LEVEL_WARNING = 4
+};
+
+
+#ifdef CONFIG_DEBUG_SYSLOG
+
+void wpa_debug_open_syslog(void);
+void wpa_debug_close_syslog(void);
+
+#else /* CONFIG_DEBUG_SYSLOG */
+
+static inline void wpa_debug_open_syslog(void)
+{
+}
+
+static inline void wpa_debug_close_syslog(void)
+{
+}
+
+#endif /* CONFIG_DEBUG_SYSLOG */
+
+#ifdef CONFIG_DEBUG_LINUX_TRACING
+
+int wpa_debug_open_linux_tracing(void);
+void wpa_debug_close_linux_tracing(void);
+
+#else /* CONFIG_DEBUG_LINUX_TRACING */
+
+static inline int wpa_debug_open_linux_tracing(void)
+{
+	return 0;
+}
+
+static inline void wpa_debug_close_linux_tracing(void)
+{
+}
+
+#endif /* CONFIG_DEBUG_LINUX_TRACING */
+
+
+#ifdef EAPOL_TEST
+#define WPA_ASSERT(a)						       \
+	do {							       \
+		if (!(a)) {					       \
+			printf("WPA_ASSERT FAILED '" #a "' "	       \
+			       "%s %s:%d\n",			       \
+			       __FUNCTION__, __FILE__, __LINE__);      \
+			exit(1);				       \
+		}						       \
+	} while (0)
+#else
+#define WPA_ASSERT(a) do { } while (0)
+#endif
+
+const char * debug_level_str(int level);
+int str_to_debug_level(const char *s);
+
+#endif /* WPA_DEBUG_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/wpabuf.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/wpabuf.c
new file mode 100644
index 0000000..77ee472
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/wpabuf.c
@@ -0,0 +1,340 @@
+/*
+ * Dynamic data buffer
+ * Copyright (c) 2007-2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "trace.h"
+#include "wpabuf.h"
+
+#ifdef WPA_TRACE
+#define WPABUF_MAGIC 0x51a974e3
+
+struct wpabuf_trace {
+	unsigned int magic;
+} __attribute__((aligned(8)));
+
+static struct wpabuf_trace * wpabuf_get_trace(const struct wpabuf *buf)
+{
+	return (struct wpabuf_trace *)
+		((const u8 *) buf - sizeof(struct wpabuf_trace));
+}
+#endif /* WPA_TRACE */
+
+
+static void wpabuf_overflow(const struct wpabuf *buf, size_t len)
+{
+#ifdef WPA_TRACE
+	struct wpabuf_trace *trace = wpabuf_get_trace(buf);
+	if (trace->magic != WPABUF_MAGIC) {
+		wpa_printf(MSG_ERROR, "wpabuf: invalid magic %x",
+			   trace->magic);
+	}
+#endif /* WPA_TRACE */
+	wpa_printf(MSG_ERROR, "wpabuf %p (size=%lu used=%lu) overflow len=%lu",
+		   buf, (unsigned long) buf->size, (unsigned long) buf->used,
+		   (unsigned long) len);
+	wpa_trace_show("wpabuf overflow");
+	abort();
+}
+
+
+int wpabuf_resize(struct wpabuf **_buf, size_t add_len)
+{
+	struct wpabuf *buf = *_buf;
+#ifdef WPA_TRACE
+	struct wpabuf_trace *trace;
+#endif /* WPA_TRACE */
+
+	if (buf == NULL) {
+		*_buf = wpabuf_alloc(add_len);
+		return *_buf == NULL ? -1 : 0;
+	}
+
+#ifdef WPA_TRACE
+	trace = wpabuf_get_trace(buf);
+	if (trace->magic != WPABUF_MAGIC) {
+		wpa_printf(MSG_ERROR, "wpabuf: invalid magic %x",
+			   trace->magic);
+		wpa_trace_show("wpabuf_resize invalid magic");
+		abort();
+	}
+#endif /* WPA_TRACE */
+
+	if (buf->used + add_len > buf->size) {
+		unsigned char *nbuf;
+		if (buf->flags & WPABUF_FLAG_EXT_DATA) {
+			nbuf = os_realloc(buf->buf, buf->used + add_len);
+			if (nbuf == NULL)
+				return -1;
+			os_memset(nbuf + buf->used, 0, add_len);
+			buf->buf = nbuf;
+		} else {
+#ifdef WPA_TRACE
+			nbuf = os_realloc(trace, sizeof(struct wpabuf_trace) +
+					  sizeof(struct wpabuf) +
+					  buf->used + add_len);
+			if (nbuf == NULL)
+				return -1;
+			trace = (struct wpabuf_trace *) nbuf;
+			buf = (struct wpabuf *) (trace + 1);
+			os_memset(nbuf + sizeof(struct wpabuf_trace) +
+				  sizeof(struct wpabuf) + buf->used, 0,
+				  add_len);
+#else /* WPA_TRACE */
+			nbuf = os_realloc(buf, sizeof(struct wpabuf) +
+					  buf->used + add_len);
+			if (nbuf == NULL)
+				return -1;
+			buf = (struct wpabuf *) nbuf;
+			os_memset(nbuf + sizeof(struct wpabuf) + buf->used, 0,
+				  add_len);
+#endif /* WPA_TRACE */
+			buf->buf = (u8 *) (buf + 1);
+			*_buf = buf;
+		}
+		buf->size = buf->used + add_len;
+	}
+
+	return 0;
+}
+
+
+/**
+ * wpabuf_alloc - Allocate a wpabuf of the given size
+ * @len: Length for the allocated buffer
+ * Returns: Buffer to the allocated wpabuf or %NULL on failure
+ */
+struct wpabuf * wpabuf_alloc(size_t len)
+{
+#ifdef WPA_TRACE
+	struct wpabuf_trace *trace = os_zalloc(sizeof(struct wpabuf_trace) +
+					       sizeof(struct wpabuf) + len);
+	struct wpabuf *buf;
+	if (trace == NULL)
+		return NULL;
+	trace->magic = WPABUF_MAGIC;
+	buf = (struct wpabuf *) (trace + 1);
+#else /* WPA_TRACE */
+	struct wpabuf *buf = os_zalloc(sizeof(struct wpabuf) + len);
+	if (buf == NULL)
+		return NULL;
+#endif /* WPA_TRACE */
+
+	buf->size = len;
+	buf->buf = (u8 *) (buf + 1);
+	return buf;
+}
+
+
+struct wpabuf * wpabuf_alloc_ext_data(u8 *data, size_t len)
+{
+#ifdef WPA_TRACE
+	struct wpabuf_trace *trace = os_zalloc(sizeof(struct wpabuf_trace) +
+					       sizeof(struct wpabuf));
+	struct wpabuf *buf;
+	if (trace == NULL)
+		return NULL;
+	trace->magic = WPABUF_MAGIC;
+	buf = (struct wpabuf *) (trace + 1);
+#else /* WPA_TRACE */
+	struct wpabuf *buf = os_zalloc(sizeof(struct wpabuf));
+	if (buf == NULL)
+		return NULL;
+#endif /* WPA_TRACE */
+
+	buf->size = len;
+	buf->used = len;
+	buf->buf = data;
+	buf->flags |= WPABUF_FLAG_EXT_DATA;
+
+	return buf;
+}
+
+
+struct wpabuf * wpabuf_alloc_copy(const void *data, size_t len)
+{
+	struct wpabuf *buf = wpabuf_alloc(len);
+	if (buf)
+		wpabuf_put_data(buf, data, len);
+	return buf;
+}
+
+
+struct wpabuf * wpabuf_dup(const struct wpabuf *src)
+{
+	struct wpabuf *buf = wpabuf_alloc(wpabuf_len(src));
+	if (buf)
+		wpabuf_put_data(buf, wpabuf_head(src), wpabuf_len(src));
+	return buf;
+}
+
+
+/**
+ * wpabuf_free - Free a wpabuf
+ * @buf: wpabuf buffer
+ */
+void wpabuf_free(struct wpabuf *buf)
+{
+#ifdef WPA_TRACE
+	struct wpabuf_trace *trace;
+	if (buf == NULL)
+		return;
+	trace = wpabuf_get_trace(buf);
+	if (trace->magic != WPABUF_MAGIC) {
+		wpa_printf(MSG_ERROR, "wpabuf_free: invalid magic %x",
+			   trace->magic);
+		wpa_trace_show("wpabuf_free magic mismatch");
+		abort();
+	}
+	if (buf->flags & WPABUF_FLAG_EXT_DATA)
+		os_free(buf->buf);
+	os_free(trace);
+#else /* WPA_TRACE */
+	if (buf == NULL)
+		return;
+	if (buf->flags & WPABUF_FLAG_EXT_DATA)
+		os_free(buf->buf);
+	os_free(buf);
+#endif /* WPA_TRACE */
+}
+
+
+void wpabuf_clear_free(struct wpabuf *buf)
+{
+	if (buf) {
+		os_memset(wpabuf_mhead(buf), 0, wpabuf_len(buf));
+		wpabuf_free(buf);
+	}
+}
+
+
+void * wpabuf_put(struct wpabuf *buf, size_t len)
+{
+	void *tmp = wpabuf_mhead_u8(buf) + wpabuf_len(buf);
+	buf->used += len;
+	if (buf->used > buf->size) {
+		wpabuf_overflow(buf, len);
+	}
+	return tmp;
+}
+
+
+/**
+ * wpabuf_concat - Concatenate two buffers into a newly allocated one
+ * @a: First buffer
+ * @b: Second buffer
+ * Returns: wpabuf with concatenated a + b data or %NULL on failure
+ *
+ * Both buffers a and b will be freed regardless of the return value. Input
+ * buffers can be %NULL which is interpreted as an empty buffer.
+ */
+struct wpabuf * wpabuf_concat(struct wpabuf *a, struct wpabuf *b)
+{
+	struct wpabuf *n = NULL;
+	size_t len = 0;
+
+	if (b == NULL)
+		return a;
+
+	if (a)
+		len += wpabuf_len(a);
+	len += wpabuf_len(b);
+
+	n = wpabuf_alloc(len);
+	if (n) {
+		if (a)
+			wpabuf_put_buf(n, a);
+		wpabuf_put_buf(n, b);
+	}
+
+	wpabuf_free(a);
+	wpabuf_free(b);
+
+	return n;
+}
+
+
+/**
+ * wpabuf_zeropad - Pad buffer with 0x00 octets (prefix) to specified length
+ * @buf: Buffer to be padded
+ * @len: Length for the padded buffer
+ * Returns: wpabuf padded to len octets or %NULL on failure
+ *
+ * If buf is longer than len octets or of same size, it will be returned as-is.
+ * Otherwise a new buffer is allocated and prefixed with 0x00 octets followed
+ * by the source data. The source buffer will be freed on error, i.e., caller
+ * will only be responsible on freeing the returned buffer. If buf is %NULL,
+ * %NULL will be returned.
+ */
+struct wpabuf * wpabuf_zeropad(struct wpabuf *buf, size_t len)
+{
+	struct wpabuf *ret;
+	size_t blen;
+
+	if (buf == NULL)
+		return NULL;
+
+	blen = wpabuf_len(buf);
+	if (blen >= len)
+		return buf;
+
+	ret = wpabuf_alloc(len);
+	if (ret) {
+		os_memset(wpabuf_put(ret, len - blen), 0, len - blen);
+		wpabuf_put_buf(ret, buf);
+	}
+	wpabuf_free(buf);
+
+	return ret;
+}
+
+
+void wpabuf_printf(struct wpabuf *buf, char *fmt, ...)
+{
+	va_list ap;
+	void *tmp = wpabuf_mhead_u8(buf) + wpabuf_len(buf);
+	int res;
+
+	va_start(ap, fmt);
+	res = vsnprintf(tmp, buf->size - buf->used, fmt, ap);
+	va_end(ap);
+	if (res < 0 || (size_t) res >= buf->size - buf->used)
+		wpabuf_overflow(buf, res);
+	buf->used += res;
+}
+
+
+/**
+ * wpabuf_parse_bin - Parse a null terminated string of binary data to a wpabuf
+ * @buf: Buffer with null terminated string (hexdump) of binary data
+ * Returns: wpabuf or %NULL on failure
+ *
+ * The string len must be a multiple of two and contain only hexadecimal digits.
+ */
+struct wpabuf * wpabuf_parse_bin(const char *buf)
+{
+	size_t len;
+	struct wpabuf *ret;
+
+	len = os_strlen(buf);
+	if (len & 0x01)
+		return NULL;
+	len /= 2;
+
+	ret = wpabuf_alloc(len);
+	if (ret == NULL)
+		return NULL;
+
+	if (hexstr2bin(buf, wpabuf_put(ret, len), len)) {
+		wpabuf_free(ret);
+		return NULL;
+	}
+
+	return ret;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/wpabuf.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/wpabuf.h
new file mode 100644
index 0000000..eb1db80
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/wpabuf.h
@@ -0,0 +1,191 @@
+/*
+ * Dynamic data buffer
+ * Copyright (c) 2007-2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WPABUF_H
+#define WPABUF_H
+
+/* wpabuf::buf is a pointer to external data */
+#define WPABUF_FLAG_EXT_DATA BIT(0)
+
+/*
+ * Internal data structure for wpabuf. Please do not touch this directly from
+ * elsewhere. This is only defined in header file to allow inline functions
+ * from this file to access data.
+ */
+struct wpabuf {
+	size_t size; /* total size of the allocated buffer */
+	size_t used; /* length of data in the buffer */
+	u8 *buf; /* pointer to the head of the buffer */
+	unsigned int flags;
+	/* optionally followed by the allocated buffer */
+};
+
+
+int wpabuf_resize(struct wpabuf **buf, size_t add_len);
+struct wpabuf * wpabuf_alloc(size_t len);
+struct wpabuf * wpabuf_alloc_ext_data(u8 *data, size_t len);
+struct wpabuf * wpabuf_alloc_copy(const void *data, size_t len);
+struct wpabuf * wpabuf_dup(const struct wpabuf *src);
+void wpabuf_free(struct wpabuf *buf);
+void wpabuf_clear_free(struct wpabuf *buf);
+void * wpabuf_put(struct wpabuf *buf, size_t len);
+struct wpabuf * wpabuf_concat(struct wpabuf *a, struct wpabuf *b);
+struct wpabuf * wpabuf_zeropad(struct wpabuf *buf, size_t len);
+void wpabuf_printf(struct wpabuf *buf, char *fmt, ...) PRINTF_FORMAT(2, 3);
+struct wpabuf * wpabuf_parse_bin(const char *buf);
+
+
+/**
+ * wpabuf_size - Get the currently allocated size of a wpabuf buffer
+ * @buf: wpabuf buffer
+ * Returns: Currently allocated size of the buffer
+ */
+static inline size_t wpabuf_size(const struct wpabuf *buf)
+{
+	return buf->size;
+}
+
+/**
+ * wpabuf_len - Get the current length of a wpabuf buffer data
+ * @buf: wpabuf buffer
+ * Returns: Currently used length of the buffer
+ */
+static inline size_t wpabuf_len(const struct wpabuf *buf)
+{
+	return buf->used;
+}
+
+/**
+ * wpabuf_tailroom - Get size of available tail room in the end of the buffer
+ * @buf: wpabuf buffer
+ * Returns: Tail room (in bytes) of available space in the end of the buffer
+ */
+static inline size_t wpabuf_tailroom(const struct wpabuf *buf)
+{
+	return buf->size - buf->used;
+}
+
+/**
+ * wpabuf_cmp - Check if two buffers contain the same data
+ * @a: wpabuf buffer
+ * @b: wpabuf buffer
+ * Returns: 0 if the two buffers contain the same data and non-zero otherwise
+ */
+static inline int wpabuf_cmp(const struct wpabuf *a, const struct wpabuf *b)
+{
+	if (!a && !b)
+		return 0;
+	if (a && b && wpabuf_size(a) == wpabuf_size(b))
+		return os_memcmp(a->buf, b->buf, wpabuf_size(a));
+	return -1;
+}
+
+/**
+ * wpabuf_head - Get pointer to the head of the buffer data
+ * @buf: wpabuf buffer
+ * Returns: Pointer to the head of the buffer data
+ */
+static inline const void * wpabuf_head(const struct wpabuf *buf)
+{
+	return buf->buf;
+}
+
+static inline const u8 * wpabuf_head_u8(const struct wpabuf *buf)
+{
+	return (const u8 *) wpabuf_head(buf);
+}
+
+/**
+ * wpabuf_mhead - Get modifiable pointer to the head of the buffer data
+ * @buf: wpabuf buffer
+ * Returns: Pointer to the head of the buffer data
+ */
+static inline void * wpabuf_mhead(struct wpabuf *buf)
+{
+	return buf->buf;
+}
+
+static inline u8 * wpabuf_mhead_u8(struct wpabuf *buf)
+{
+	return (u8 *) wpabuf_mhead(buf);
+}
+
+static inline void wpabuf_put_u8(struct wpabuf *buf, u8 data)
+{
+	u8 *pos = (u8 *) wpabuf_put(buf, 1);
+	*pos = data;
+}
+
+static inline void wpabuf_put_le16(struct wpabuf *buf, u16 data)
+{
+	u8 *pos = (u8 *) wpabuf_put(buf, 2);
+	WPA_PUT_LE16(pos, data);
+}
+
+static inline void wpabuf_put_le32(struct wpabuf *buf, u32 data)
+{
+	u8 *pos = (u8 *) wpabuf_put(buf, 4);
+	WPA_PUT_LE32(pos, data);
+}
+
+static inline void wpabuf_put_le64(struct wpabuf *buf, u64 data)
+{
+	u8 *pos = (u8 *) wpabuf_put(buf, 8);
+	WPA_PUT_LE64(pos, data);
+}
+
+static inline void wpabuf_put_be16(struct wpabuf *buf, u16 data)
+{
+	u8 *pos = (u8 *) wpabuf_put(buf, 2);
+	WPA_PUT_BE16(pos, data);
+}
+
+static inline void wpabuf_put_be24(struct wpabuf *buf, u32 data)
+{
+	u8 *pos = (u8 *) wpabuf_put(buf, 3);
+	WPA_PUT_BE24(pos, data);
+}
+
+static inline void wpabuf_put_be32(struct wpabuf *buf, u32 data)
+{
+	u8 *pos = (u8 *) wpabuf_put(buf, 4);
+	WPA_PUT_BE32(pos, data);
+}
+
+static inline void wpabuf_put_be64(struct wpabuf *buf, u64 data)
+{
+	u8 *pos = (u8 *) wpabuf_put(buf, 8);
+	WPA_PUT_BE64(pos, data);
+}
+
+static inline void wpabuf_put_data(struct wpabuf *buf, const void *data,
+				   size_t len)
+{
+	if (data)
+		os_memcpy(wpabuf_put(buf, len), data, len);
+}
+
+static inline void wpabuf_put_buf(struct wpabuf *dst,
+				  const struct wpabuf *src)
+{
+	wpabuf_put_data(dst, wpabuf_head(src), wpabuf_len(src));
+}
+
+static inline void wpabuf_set(struct wpabuf *buf, const void *data, size_t len)
+{
+	buf->buf = (u8 *) data;
+	buf->flags = WPABUF_FLAG_EXT_DATA;
+	buf->size = buf->used = len;
+}
+
+static inline void wpabuf_put_str(struct wpabuf *dst, const char *str)
+{
+	wpabuf_put_data(dst, str, os_strlen(str));
+}
+
+#endif /* WPABUF_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/xml-utils.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/xml-utils.c
new file mode 100644
index 0000000..dae91fe
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/xml-utils.c
@@ -0,0 +1,471 @@
+/*
+ * Generic XML helper functions
+ * Copyright (c) 2012-2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "xml-utils.h"
+
+
+static xml_node_t * get_node_uri_iter(struct xml_node_ctx *ctx,
+				      xml_node_t *root, char *uri)
+{
+	char *end;
+	xml_node_t *node;
+	const char *name;
+
+	end = strchr(uri, '/');
+	if (end)
+		*end++ = '\0';
+
+	node = root;
+	xml_node_for_each_sibling(ctx, node) {
+		xml_node_for_each_check(ctx, node);
+		name = xml_node_get_localname(ctx, node);
+		if (strcasecmp(name, uri) == 0)
+			break;
+	}
+
+	if (node == NULL)
+		return NULL;
+
+	if (end) {
+		return get_node_uri_iter(ctx, xml_node_first_child(ctx, node),
+					 end);
+	}
+
+	return node;
+}
+
+
+xml_node_t * get_node_uri(struct xml_node_ctx *ctx, xml_node_t *root,
+			  const char *uri)
+{
+	char *search;
+	xml_node_t *node;
+
+	search = os_strdup(uri);
+	if (search == NULL)
+		return NULL;
+
+	node = get_node_uri_iter(ctx, root, search);
+
+	os_free(search);
+	return node;
+}
+
+
+static xml_node_t * get_node_iter(struct xml_node_ctx *ctx,
+				  xml_node_t *root, const char *path)
+{
+	char *end;
+	xml_node_t *node;
+	const char *name;
+
+	end = os_strchr(path, '/');
+	if (end)
+		*end++ = '\0';
+
+	xml_node_for_each_child(ctx, node, root) {
+		xml_node_for_each_check(ctx, node);
+		name = xml_node_get_localname(ctx, node);
+		if (os_strcasecmp(name, path) == 0)
+			break;
+	}
+
+	if (node == NULL)
+		return NULL;
+	if (end)
+		return get_node_iter(ctx, node, end);
+	return node;
+}
+
+
+xml_node_t * get_node(struct xml_node_ctx *ctx, xml_node_t *root,
+		      const char *path)
+{
+	char *search;
+	xml_node_t *node;
+
+	search = os_strdup(path);
+	if (search == NULL)
+		return NULL;
+
+	node = get_node_iter(ctx, root, search);
+
+	os_free(search);
+	return node;
+}
+
+
+xml_node_t * get_child_node(struct xml_node_ctx *ctx, xml_node_t *root,
+			    const char *path)
+{
+	xml_node_t *node;
+	xml_node_t *match;
+
+	xml_node_for_each_child(ctx, node, root) {
+		xml_node_for_each_check(ctx, node);
+		match = get_node(ctx, node, path);
+		if (match)
+			return match;
+	}
+
+	return NULL;
+}
+
+
+xml_node_t * node_from_file(struct xml_node_ctx *ctx, const char *name)
+{
+	xml_node_t *node;
+	char *buf, *buf2, *start;
+	size_t len;
+
+	buf = os_readfile(name, &len);
+	if (buf == NULL)
+		return NULL;
+	buf2 = os_realloc(buf, len + 1);
+	if (buf2 == NULL) {
+		os_free(buf);
+		return NULL;
+	}
+	buf = buf2;
+	buf[len] = '\0';
+
+	start = os_strstr(buf, "<!DOCTYPE ");
+	if (start) {
+		char *pos = start + 1;
+		int count = 1;
+		while (*pos) {
+			if (*pos == '<')
+				count++;
+			else if (*pos == '>') {
+				count--;
+				if (count == 0) {
+					pos++;
+					break;
+				}
+			}
+			pos++;
+		}
+		if (count == 0) {
+			/* Remove DOCTYPE to allow the file to be parsed */
+			os_memset(start, ' ', pos - start);
+		}
+	}
+
+	node = xml_node_from_buf(ctx, buf);
+	os_free(buf);
+
+	return node;
+}
+
+
+int node_to_file(struct xml_node_ctx *ctx, const char *fname, xml_node_t *node)
+{
+	FILE *f;
+	char *str;
+
+	str = xml_node_to_str(ctx, node);
+	if (str == NULL)
+		return -1;
+
+	f = fopen(fname, "w");
+	if (!f) {
+		os_free(str);
+		return -1;
+	}
+
+	fprintf(f, "%s\n", str);
+	os_free(str);
+	fclose(f);
+
+	return 0;
+}
+
+
+static char * get_val(struct xml_node_ctx *ctx, xml_node_t *node)
+{
+	char *val, *pos;
+
+	val = xml_node_get_text(ctx, node);
+	if (val == NULL)
+		return NULL;
+	pos = val;
+	while (*pos) {
+		if (*pos != ' ' && *pos != '\t' && *pos != '\r' && *pos != '\n')
+			return val;
+		pos++;
+	}
+
+	return NULL;
+}
+
+
+static char * add_path(const char *prev, const char *leaf)
+{
+	size_t len;
+	char *new_uri;
+
+	if (prev == NULL)
+		return NULL;
+
+	len = os_strlen(prev) + 1 + os_strlen(leaf) + 1;
+	new_uri = os_malloc(len);
+	if (new_uri)
+		os_snprintf(new_uri, len, "%s/%s", prev, leaf);
+
+	return new_uri;
+}
+
+
+static void node_to_tnds(struct xml_node_ctx *ctx, xml_node_t *out,
+			 xml_node_t *in, const char *uri)
+{
+	xml_node_t *node;
+	xml_node_t *tnds;
+	const char *name;
+	char *val;
+	char *new_uri;
+
+	xml_node_for_each_child(ctx, node, in) {
+		xml_node_for_each_check(ctx, node);
+		name = xml_node_get_localname(ctx, node);
+
+		tnds = xml_node_create(ctx, out, NULL, "Node");
+		if (tnds == NULL)
+			return;
+		xml_node_create_text(ctx, tnds, NULL, "NodeName", name);
+
+		if (uri)
+			xml_node_create_text(ctx, tnds, NULL, "Path", uri);
+
+		val = get_val(ctx, node);
+		if (val || !xml_node_first_child(ctx, node))
+			xml_node_create_text(ctx, tnds, NULL, "Value",
+					     val ? val : "");
+		xml_node_get_text_free(ctx, val);
+
+		new_uri = add_path(uri, name);
+		node_to_tnds(ctx, new_uri ? out : tnds, node, new_uri);
+		os_free(new_uri);
+	}
+}
+
+
+static int add_ddfname(struct xml_node_ctx *ctx, xml_node_t *parent,
+		       const char *urn)
+{
+	xml_node_t *node;
+
+	node = xml_node_create(ctx, parent, NULL, "RTProperties");
+	if (node == NULL)
+		return -1;
+	node = xml_node_create(ctx, node, NULL, "Type");
+	if (node == NULL)
+		return -1;
+	xml_node_create_text(ctx, node, NULL, "DDFName", urn);
+	return 0;
+}
+
+
+xml_node_t * mo_to_tnds(struct xml_node_ctx *ctx, xml_node_t *mo,
+			int use_path, const char *urn, const char *ns_uri)
+{
+	xml_node_t *root;
+	xml_node_t *node;
+	const char *name;
+
+	root = xml_node_create_root(ctx, ns_uri, NULL, NULL, "MgmtTree");
+	if (root == NULL)
+		return NULL;
+
+	xml_node_create_text(ctx, root, NULL, "VerDTD", "1.2");
+
+	name = xml_node_get_localname(ctx, mo);
+
+	node = xml_node_create(ctx, root, NULL, "Node");
+	if (node == NULL)
+		goto fail;
+	xml_node_create_text(ctx, node, NULL, "NodeName", name);
+	if (urn)
+		add_ddfname(ctx, node, urn);
+
+	node_to_tnds(ctx, use_path ? root : node, mo, use_path ? name : NULL);
+
+	return root;
+
+fail:
+	xml_node_free(ctx, root);
+	return NULL;
+}
+
+
+static xml_node_t * get_first_child_node(struct xml_node_ctx *ctx,
+					 xml_node_t *node,
+					 const char *name)
+{
+	const char *lname;
+	xml_node_t *child;
+
+	xml_node_for_each_child(ctx, child, node) {
+		xml_node_for_each_check(ctx, child);
+		lname = xml_node_get_localname(ctx, child);
+		if (os_strcasecmp(lname, name) == 0)
+			return child;
+	}
+
+	return NULL;
+}
+
+
+static char * get_node_text(struct xml_node_ctx *ctx, xml_node_t *node,
+			    const char *node_name)
+{
+	node = get_first_child_node(ctx, node, node_name);
+	if (node == NULL)
+		return NULL;
+	return xml_node_get_text(ctx, node);
+}
+
+
+static xml_node_t * add_mo_node(struct xml_node_ctx *ctx, xml_node_t *root,
+				xml_node_t *node, const char *uri)
+{
+	char *nodename, *value, *path;
+	xml_node_t *parent;
+
+	nodename = get_node_text(ctx, node, "NodeName");
+	if (nodename == NULL)
+		return NULL;
+	value = get_node_text(ctx, node, "Value");
+
+	if (root == NULL) {
+		root = xml_node_create_root(ctx, NULL, NULL, NULL,
+					    nodename);
+		if (root && value)
+			xml_node_set_text(ctx, root, value);
+	} else {
+		if (uri == NULL) {
+			xml_node_get_text_free(ctx, nodename);
+			xml_node_get_text_free(ctx, value);
+			return NULL;
+		}
+		path = get_node_text(ctx, node, "Path");
+		if (path)
+			uri = path;
+		parent = get_node_uri(ctx, root, uri);
+		xml_node_get_text_free(ctx, path);
+		if (parent == NULL) {
+			printf("Could not find URI '%s'\n", uri);
+			xml_node_get_text_free(ctx, nodename);
+			xml_node_get_text_free(ctx, value);
+			return NULL;
+		}
+		if (value)
+			xml_node_create_text(ctx, parent, NULL, nodename,
+					     value);
+		else
+			xml_node_create(ctx, parent, NULL, nodename);
+	}
+
+	xml_node_get_text_free(ctx, nodename);
+	xml_node_get_text_free(ctx, value);
+
+	return root;
+}
+
+
+static xml_node_t * tnds_to_mo_iter(struct xml_node_ctx *ctx, xml_node_t *root,
+				    xml_node_t *node, const char *uri)
+{
+	xml_node_t *child;
+	const char *name;
+	char *nodename;
+
+	xml_node_for_each_sibling(ctx, node) {
+		xml_node_for_each_check(ctx, node);
+
+		nodename = get_node_text(ctx, node, "NodeName");
+		if (nodename == NULL)
+			return NULL;
+
+		name = xml_node_get_localname(ctx, node);
+		if (strcmp(name, "Node") == 0) {
+			if (root && !uri) {
+				printf("Invalid TNDS tree structure - "
+				       "multiple top level nodes\n");
+				xml_node_get_text_free(ctx, nodename);
+				return NULL;
+			}
+			root = add_mo_node(ctx, root, node, uri);
+		}
+
+		child = get_first_child_node(ctx, node, "Node");
+		if (child) {
+			if (uri == NULL)
+				tnds_to_mo_iter(ctx, root, child, nodename);
+			else {
+				char *new_uri;
+				new_uri = add_path(uri, nodename);
+				tnds_to_mo_iter(ctx, root, child, new_uri);
+				os_free(new_uri);
+			}
+		}
+		xml_node_get_text_free(ctx, nodename);
+	}
+
+	return root;
+}
+
+
+xml_node_t * tnds_to_mo(struct xml_node_ctx *ctx, xml_node_t *tnds)
+{
+	const char *name;
+	xml_node_t *node;
+
+	name = xml_node_get_localname(ctx, tnds);
+	if (name == NULL || os_strcmp(name, "MgmtTree") != 0)
+		return NULL;
+
+	node = get_first_child_node(ctx, tnds, "Node");
+	if (!node)
+		return NULL;
+	return tnds_to_mo_iter(ctx, NULL, node, NULL);
+}
+
+
+xml_node_t * soap_build_envelope(struct xml_node_ctx *ctx, xml_node_t *node)
+{
+	xml_node_t *envelope, *body;
+	xml_namespace_t *ns;
+
+	envelope = xml_node_create_root(
+		ctx, "http://www.w3.org/2003/05/soap-envelope", "soap12", &ns,
+		"Envelope");
+	if (envelope == NULL)
+		return NULL;
+	body = xml_node_create(ctx, envelope, ns, "Body");
+	xml_node_add_child(ctx, body, node);
+	return envelope;
+}
+
+
+xml_node_t * soap_get_body(struct xml_node_ctx *ctx, xml_node_t *soap)
+{
+	xml_node_t *body, *child;
+
+	body = get_node_uri(ctx, soap, "Envelope/Body");
+	if (body == NULL)
+		return NULL;
+	xml_node_for_each_child(ctx, child, body) {
+		xml_node_for_each_check(ctx, child);
+		return child;
+	}
+	return NULL;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/xml-utils.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/xml-utils.h
new file mode 100644
index 0000000..fb6208c
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/xml-utils.h
@@ -0,0 +1,97 @@
+/*
+ * Generic XML helper functions
+ * Copyright (c) 2012-2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef XML_UTILS_H
+#define XML_UTILS_H
+
+struct xml_node_ctx;
+typedef struct xml_node xml_node_t;
+typedef struct xml_namespace_foo xml_namespace_t;
+
+/* XML library wrappers */
+
+int xml_validate(struct xml_node_ctx *ctx, xml_node_t *node,
+		 const char *xml_schema_fname, char **ret_err);
+int xml_validate_dtd(struct xml_node_ctx *ctx, xml_node_t *node,
+		     const char *dtd_fname, char **ret_err);
+void xml_node_free(struct xml_node_ctx *ctx, xml_node_t *node);
+xml_node_t * xml_node_get_parent(struct xml_node_ctx *ctx, xml_node_t *node);
+xml_node_t * xml_node_from_buf(struct xml_node_ctx *ctx, const char *buf);
+const char * xml_node_get_localname(struct xml_node_ctx *ctx,
+				    xml_node_t *node);
+char * xml_node_to_str(struct xml_node_ctx *ctx, xml_node_t *node);
+void xml_node_detach(struct xml_node_ctx *ctx, xml_node_t *node);
+void xml_node_add_child(struct xml_node_ctx *ctx, xml_node_t *parent,
+			xml_node_t *child);
+xml_node_t * xml_node_create_root(struct xml_node_ctx *ctx, const char *ns_uri,
+				  const char *ns_prefix,
+				  xml_namespace_t **ret_ns, const char *name);
+xml_node_t * xml_node_create(struct xml_node_ctx *ctx, xml_node_t *parent,
+			     xml_namespace_t *ns, const char *name);
+xml_node_t * xml_node_create_text(struct xml_node_ctx *ctx,
+				  xml_node_t *parent, xml_namespace_t *ns,
+				  const char *name, const char *value);
+xml_node_t * xml_node_create_text_ns(struct xml_node_ctx *ctx,
+				     xml_node_t *parent, const char *ns_uri,
+				     const char *name, const char *value);
+void xml_node_set_text(struct xml_node_ctx *ctx, xml_node_t *node,
+		       const char *value);
+int xml_node_add_attr(struct xml_node_ctx *ctx, xml_node_t *node,
+		      xml_namespace_t *ns, const char *name, const char *value);
+char * xml_node_get_attr_value(struct xml_node_ctx *ctx, xml_node_t *node,
+			       char *name);
+char * xml_node_get_attr_value_ns(struct xml_node_ctx *ctx, xml_node_t *node,
+				  const char *ns_uri, char *name);
+void xml_node_get_attr_value_free(struct xml_node_ctx *ctx, char *val);
+xml_node_t * xml_node_first_child(struct xml_node_ctx *ctx,
+				  xml_node_t *parent);
+xml_node_t * xml_node_next_sibling(struct xml_node_ctx *ctx,
+				   xml_node_t *node);
+int xml_node_is_element(struct xml_node_ctx *ctx, xml_node_t *node);
+char * xml_node_get_text(struct xml_node_ctx *ctx, xml_node_t *node);
+void xml_node_get_text_free(struct xml_node_ctx *ctx, char *val);
+char * xml_node_get_base64_text(struct xml_node_ctx *ctx, xml_node_t *node,
+				int *ret_len);
+xml_node_t * xml_node_copy(struct xml_node_ctx *ctx, xml_node_t *node);
+
+#define xml_node_for_each_child(ctx, child, parent) \
+for (child = xml_node_first_child(ctx, parent); \
+     child; \
+     child = xml_node_next_sibling(ctx, child))
+
+#define xml_node_for_each_sibling(ctx, node) \
+for (; \
+     node; \
+     node = xml_node_next_sibling(ctx, node))
+
+#define xml_node_for_each_check(ctx, child) \
+if (!xml_node_is_element(ctx, child)) \
+	continue
+
+
+struct xml_node_ctx * xml_node_init_ctx(void *upper_ctx,
+					const void *env);
+void xml_node_deinit_ctx(struct xml_node_ctx *ctx);
+
+
+xml_node_t * get_node_uri(struct xml_node_ctx *ctx, xml_node_t *root,
+			  const char *uri);
+xml_node_t * get_node(struct xml_node_ctx *ctx, xml_node_t *root,
+		      const char *path);
+xml_node_t * get_child_node(struct xml_node_ctx *ctx, xml_node_t *root,
+			    const char *path);
+xml_node_t * node_from_file(struct xml_node_ctx *ctx, const char *name);
+int node_to_file(struct xml_node_ctx *ctx, const char *fname, xml_node_t *node);
+xml_node_t * mo_to_tnds(struct xml_node_ctx *ctx, xml_node_t *mo,
+			int use_path, const char *urn, const char *ns_uri);
+xml_node_t * tnds_to_mo(struct xml_node_ctx *ctx, xml_node_t *tnds);
+
+xml_node_t * soap_build_envelope(struct xml_node_ctx *ctx, xml_node_t *node);
+xml_node_t * soap_get_body(struct xml_node_ctx *ctx, xml_node_t *soap);
+
+#endif /* XML_UTILS_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/xml_libxml2.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/xml_libxml2.c
new file mode 100644
index 0000000..d73654e
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/utils/xml_libxml2.c
@@ -0,0 +1,459 @@
+/*
+ * XML wrapper for libxml2
+ * Copyright (c) 2012-2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#define LIBXML_VALID_ENABLED
+#include <libxml/tree.h>
+#include <libxml/xmlschemastypes.h>
+
+#include "common.h"
+#include "base64.h"
+#include "xml-utils.h"
+
+
+struct xml_node_ctx {
+	void *ctx;
+};
+
+
+struct str_buf {
+	char *buf;
+	size_t len;
+};
+
+#define MAX_STR 1000
+
+static void add_str(void *ctx_ptr, const char *fmt, ...)
+{
+	struct str_buf *str = ctx_ptr;
+	va_list ap;
+	char *n;
+	int len;
+
+	n = os_realloc(str->buf, str->len + MAX_STR + 2);
+	if (n == NULL)
+		return;
+	str->buf = n;
+
+	va_start(ap, fmt);
+	len = vsnprintf(str->buf + str->len, MAX_STR, fmt, ap);
+	va_end(ap);
+	if (len >= MAX_STR)
+		len = MAX_STR - 1;
+	str->len += len;
+	str->buf[str->len] = '\0';
+}
+
+
+int xml_validate(struct xml_node_ctx *ctx, xml_node_t *node,
+		 const char *xml_schema_fname, char **ret_err)
+{
+	xmlDocPtr doc;
+	xmlNodePtr n;
+	xmlSchemaParserCtxtPtr pctx;
+	xmlSchemaValidCtxtPtr vctx;
+	xmlSchemaPtr schema;
+	int ret;
+	struct str_buf errors;
+
+	if (ret_err)
+		*ret_err = NULL;
+
+	doc = xmlNewDoc((xmlChar *) "1.0");
+	if (doc == NULL)
+		return -1;
+	n = xmlDocCopyNode((xmlNodePtr) node, doc, 1);
+	if (n == NULL) {
+		xmlFreeDoc(doc);
+		return -1;
+	}
+	xmlDocSetRootElement(doc, n);
+
+	os_memset(&errors, 0, sizeof(errors));
+
+	pctx = xmlSchemaNewParserCtxt(xml_schema_fname);
+	xmlSchemaSetParserErrors(pctx, (xmlSchemaValidityErrorFunc) add_str,
+				 (xmlSchemaValidityWarningFunc) add_str,
+				 &errors);
+	schema = xmlSchemaParse(pctx);
+	xmlSchemaFreeParserCtxt(pctx);
+
+	vctx = xmlSchemaNewValidCtxt(schema);
+	xmlSchemaSetValidErrors(vctx, (xmlSchemaValidityErrorFunc) add_str,
+				(xmlSchemaValidityWarningFunc) add_str,
+				&errors);
+
+	ret = xmlSchemaValidateDoc(vctx, doc);
+	xmlSchemaFreeValidCtxt(vctx);
+	xmlFreeDoc(doc);
+	xmlSchemaFree(schema);
+
+	if (ret == 0) {
+		os_free(errors.buf);
+		return 0;
+	} else if (ret > 0) {
+		if (ret_err)
+			*ret_err = errors.buf;
+		else
+			os_free(errors.buf);
+		return -1;
+	} else {
+		if (ret_err)
+			*ret_err = errors.buf;
+		else
+			os_free(errors.buf);
+		return -1;
+	}
+}
+
+
+int xml_validate_dtd(struct xml_node_ctx *ctx, xml_node_t *node,
+		     const char *dtd_fname, char **ret_err)
+{
+	xmlDocPtr doc;
+	xmlNodePtr n;
+	xmlValidCtxt vctx;
+	xmlDtdPtr dtd;
+	int ret;
+	struct str_buf errors;
+
+	if (ret_err)
+		*ret_err = NULL;
+
+	doc = xmlNewDoc((xmlChar *) "1.0");
+	if (doc == NULL)
+		return -1;
+	n = xmlDocCopyNode((xmlNodePtr) node, doc, 1);
+	if (n == NULL) {
+		xmlFreeDoc(doc);
+		return -1;
+	}
+	xmlDocSetRootElement(doc, n);
+
+	os_memset(&errors, 0, sizeof(errors));
+
+	dtd = xmlParseDTD(NULL, (const xmlChar *) dtd_fname);
+	if (dtd == NULL) {
+		xmlFreeDoc(doc);
+		return -1;
+	}
+
+	os_memset(&vctx, 0, sizeof(vctx));
+	vctx.userData = &errors;
+	vctx.error = add_str;
+	vctx.warning = add_str;
+	ret = xmlValidateDtd(&vctx, doc, dtd);
+	xmlFreeDoc(doc);
+	xmlFreeDtd(dtd);
+
+	if (ret == 1) {
+		os_free(errors.buf);
+		return 0;
+	} else {
+		if (ret_err)
+			*ret_err = errors.buf;
+		else
+			os_free(errors.buf);
+		return -1;
+	}
+}
+
+
+void xml_node_free(struct xml_node_ctx *ctx, xml_node_t *node)
+{
+	xmlFreeNode((xmlNodePtr) node);
+}
+
+
+xml_node_t * xml_node_get_parent(struct xml_node_ctx *ctx, xml_node_t *node)
+{
+	return (xml_node_t *) ((xmlNodePtr) node)->parent;
+}
+
+
+xml_node_t * xml_node_from_buf(struct xml_node_ctx *ctx, const char *buf)
+{
+	xmlDocPtr doc;
+	xmlNodePtr node;
+
+	doc = xmlParseMemory(buf, strlen(buf));
+	if (doc == NULL)
+		return NULL;
+	node = xmlDocGetRootElement(doc);
+	node = xmlCopyNode(node, 1);
+	xmlFreeDoc(doc);
+
+	return (xml_node_t *) node;
+}
+
+
+const char * xml_node_get_localname(struct xml_node_ctx *ctx,
+				    xml_node_t *node)
+{
+	return (const char *) ((xmlNodePtr) node)->name;
+}
+
+
+char * xml_node_to_str(struct xml_node_ctx *ctx, xml_node_t *node)
+{
+	xmlChar *buf;
+	int bufsiz;
+	char *ret, *pos;
+	xmlNodePtr n = (xmlNodePtr) node;
+	xmlDocPtr doc;
+
+	doc = xmlNewDoc((xmlChar *) "1.0");
+	n = xmlDocCopyNode(n, doc, 1);
+	xmlDocSetRootElement(doc, n);
+	xmlDocDumpFormatMemory(doc, &buf, &bufsiz, 0);
+	xmlFreeDoc(doc);
+	if (!buf)
+		return NULL;
+	pos = (char *) buf;
+	if (strncmp(pos, "<?xml", 5) == 0) {
+		pos = strchr(pos, '>');
+		if (pos)
+			pos++;
+		while (pos && (*pos == '\r' || *pos == '\n'))
+			pos++;
+	}
+	if (pos)
+		ret = os_strdup(pos);
+	else
+		ret = NULL;
+	xmlFree(buf);
+
+	if (ret) {
+		pos = ret;
+		if (pos[0]) {
+			while (pos[1])
+				pos++;
+		}
+		while (pos >= ret && *pos == '\n')
+			*pos-- = '\0';
+	}
+
+	return ret;
+}
+
+
+void xml_node_detach(struct xml_node_ctx *ctx, xml_node_t *node)
+{
+	xmlUnlinkNode((xmlNodePtr) node);
+}
+
+
+void xml_node_add_child(struct xml_node_ctx *ctx, xml_node_t *parent,
+			xml_node_t *child)
+{
+	xmlAddChild((xmlNodePtr) parent, (xmlNodePtr) child);
+}
+
+
+xml_node_t * xml_node_create_root(struct xml_node_ctx *ctx, const char *ns_uri,
+				  const char *ns_prefix,
+				  xml_namespace_t **ret_ns, const char *name)
+{
+	xmlNodePtr node;
+	xmlNsPtr ns = NULL;
+
+	node = xmlNewNode(NULL, (const xmlChar *) name);
+	if (node == NULL)
+		return NULL;
+	if (ns_uri) {
+		ns = xmlNewNs(node, (const xmlChar *) ns_uri,
+			      (const xmlChar *) ns_prefix);
+		xmlSetNs(node, ns);
+	}
+
+	if (ret_ns)
+		*ret_ns = (xml_namespace_t *) ns;
+
+	return (xml_node_t *) node;
+}
+
+
+xml_node_t * xml_node_create(struct xml_node_ctx *ctx, xml_node_t *parent,
+			     xml_namespace_t *ns, const char *name)
+{
+	xmlNodePtr node;
+	node = xmlNewChild((xmlNodePtr) parent, (xmlNsPtr) ns,
+			   (const xmlChar *) name, NULL);
+	return (xml_node_t *) node;
+}
+
+
+xml_node_t * xml_node_create_text(struct xml_node_ctx *ctx,
+				  xml_node_t *parent, xml_namespace_t *ns,
+				  const char *name, const char *value)
+{
+	xmlNodePtr node;
+	node = xmlNewTextChild((xmlNodePtr) parent, (xmlNsPtr) ns,
+			       (const xmlChar *) name, (const xmlChar *) value);
+	return (xml_node_t *) node;
+}
+
+
+xml_node_t * xml_node_create_text_ns(struct xml_node_ctx *ctx,
+				     xml_node_t *parent, const char *ns_uri,
+				     const char *name, const char *value)
+{
+	xmlNodePtr node;
+	xmlNsPtr ns;
+
+	node = xmlNewTextChild((xmlNodePtr) parent, NULL,
+			       (const xmlChar *) name, (const xmlChar *) value);
+	ns = xmlNewNs(node, (const xmlChar *) ns_uri, NULL);
+	xmlSetNs(node, ns);
+	return (xml_node_t *) node;
+}
+
+
+void xml_node_set_text(struct xml_node_ctx *ctx, xml_node_t *node,
+		       const char *value)
+{
+	/* TODO: escape XML special chars in value */
+	xmlNodeSetContent((xmlNodePtr) node, (xmlChar *) value);
+}
+
+
+int xml_node_add_attr(struct xml_node_ctx *ctx, xml_node_t *node,
+		      xml_namespace_t *ns, const char *name, const char *value)
+{
+	xmlAttrPtr attr;
+
+	if (ns) {
+		attr = xmlNewNsProp((xmlNodePtr) node, (xmlNsPtr) ns,
+				    (const xmlChar *) name,
+				    (const xmlChar *) value);
+	} else {
+		attr = xmlNewProp((xmlNodePtr) node, (const xmlChar *) name,
+				  (const xmlChar *) value);
+	}
+
+	return attr ? 0 : -1;
+}
+
+
+char * xml_node_get_attr_value(struct xml_node_ctx *ctx, xml_node_t *node,
+			       char *name)
+{
+	return (char *) xmlGetNoNsProp((xmlNodePtr) node,
+				       (const xmlChar *) name);
+}
+
+
+char * xml_node_get_attr_value_ns(struct xml_node_ctx *ctx, xml_node_t *node,
+				  const char *ns_uri, char *name)
+{
+	return (char *) xmlGetNsProp((xmlNodePtr) node, (const xmlChar *) name,
+				     (const xmlChar *) ns_uri);
+}
+
+
+void xml_node_get_attr_value_free(struct xml_node_ctx *ctx, char *val)
+{
+	if (val)
+		xmlFree((xmlChar *) val);
+}
+
+
+xml_node_t * xml_node_first_child(struct xml_node_ctx *ctx,
+				  xml_node_t *parent)
+{
+	return (xml_node_t *) ((xmlNodePtr) parent)->children;
+}
+
+
+xml_node_t * xml_node_next_sibling(struct xml_node_ctx *ctx,
+				   xml_node_t *node)
+{
+	return (xml_node_t *) ((xmlNodePtr) node)->next;
+}
+
+
+int xml_node_is_element(struct xml_node_ctx *ctx, xml_node_t *node)
+{
+	return ((xmlNodePtr) node)->type == XML_ELEMENT_NODE;
+}
+
+
+char * xml_node_get_text(struct xml_node_ctx *ctx, xml_node_t *node)
+{
+	if (xmlChildElementCount((xmlNodePtr) node) > 0)
+		return NULL;
+	return (char *) xmlNodeGetContent((xmlNodePtr) node);
+}
+
+
+void xml_node_get_text_free(struct xml_node_ctx *ctx, char *val)
+{
+	if (val)
+		xmlFree((xmlChar *) val);
+}
+
+
+char * xml_node_get_base64_text(struct xml_node_ctx *ctx, xml_node_t *node,
+				int *ret_len)
+{
+	char *txt;
+	unsigned char *ret;
+	size_t len;
+
+	txt = xml_node_get_text(ctx, node);
+	if (txt == NULL)
+		return NULL;
+
+	ret = base64_decode(txt, strlen(txt), &len);
+	if (ret_len)
+		*ret_len = len;
+	xml_node_get_text_free(ctx, txt);
+	if (ret == NULL)
+		return NULL;
+	txt = os_malloc(len + 1);
+	if (txt == NULL) {
+		os_free(ret);
+		return NULL;
+	}
+	os_memcpy(txt, ret, len);
+	txt[len] = '\0';
+	return txt;
+}
+
+
+xml_node_t * xml_node_copy(struct xml_node_ctx *ctx, xml_node_t *node)
+{
+	if (node == NULL)
+		return NULL;
+	return (xml_node_t *) xmlCopyNode((xmlNodePtr) node, 1);
+}
+
+
+struct xml_node_ctx * xml_node_init_ctx(void *upper_ctx,
+					const void *env)
+{
+	struct xml_node_ctx *xctx;
+
+	xctx = os_zalloc(sizeof(*xctx));
+	if (xctx == NULL)
+		return NULL;
+	xctx->ctx = upper_ctx;
+
+	LIBXML_TEST_VERSION
+
+	return xctx;
+}
+
+
+void xml_node_deinit_ctx(struct xml_node_ctx *ctx)
+{
+	xmlSchemaCleanupTypes();
+	xmlCleanupParser();
+	xmlMemoryDump();
+	os_free(ctx);
+}