Merge remote-tracking branch 'origin/caf/github-qca/master' into wlan-service.lnx.4.0

05630ab server_request_status: OSU server status for certificate enrollment
93b170b server_set_parameter: OSU server certificate configuration
dd32f19 HS 2.0: Fix ANQP request with large scan data with dev_send_frame
439352d sta_hs2_associate: Support band argument
b639f1c sta_hs2_venue_info: Venue information check and displaying
4c8681c sta_osu: Add support for osu_ssid argument
c2b9121 HS 2.0: Allow OSEN-only AP to be configured
0f1614b server_request_status: AAA result reporting
ab8c718 server_set_parameter: Add a dummy handler to accept the command
e6ac6a0 HS 2.0: Do not check OSU_SSID against WLAN_TAG2 SSID in single-BSS case
2052daa Implement dev_ble_action
55eb558 HE: Fix issue with MAC padding setting value
c658182 HE: Add support for setting ADDBA buffer size (WCN)
26e2758 HE: Change the ADDBA buffer size attribute type to U16 from U8
f89fefa Update qca-vendor_copy.h to the latest qca-vendor.h
3b17d53 server_reset_default: Database updates based on UserName
ad8a24e Remove server commands from default build
94d7b12 server_reset_default: Accept HS2-R3 as well as HS2-R2
72ac93c server: Add dummy server_ca_get_version and server_get_info commands
11ab72c DPP: Use group_id interface in place of groups_override
...

Change-Id: I52daeb280bd87e6ce349eb1196a0895868cde597
CRs-Fixed: 2316431
diff --git a/Makefile b/Makefile
index ac4f4eb..039d340 100644
--- a/Makefile
+++ b/Makefile
@@ -56,9 +56,10 @@
 OBJS += sniffer.o
 endif
 
-ifndef NO_SERVER
+ifdef SERVER
 CFLAGS += -DCONFIG_SERVER
 OBJS += server.o
+LIBS += -lsqlite3
 endif
 
 ifdef MIRACAST
diff --git a/ap.c b/ap.c
index 2326272..067b541 100644
--- a/ap.c
+++ b/ap.c
@@ -100,6 +100,15 @@
 			unsigned char is_proc_instance_one, int sig);
 
 
+static int ap_ft_enabled(struct sigma_dut *dut)
+{
+	return dut->ap_ft_oa == 1 ||
+		dut->ap_key_mgmt == AP_WPA2_FT_EAP ||
+		dut->ap_key_mgmt == AP_WPA2_FT_PSK ||
+		dut->ap_key_mgmt == AP_WPA2_ENT_FT_EAP;
+}
+
+
 static int cmd_ap_ca_version(struct sigma_dut *dut, struct sigma_conn *conn,
 			     struct sigma_cmd *cmd)
 {
@@ -1700,6 +1709,29 @@
 			dut->ap_key_mgmt = AP_WPA2_EAP_OSEN;
 			dut->ap_cipher = AP_CCMP;
 			dut->ap_pmf = AP_PMF_OPTIONAL;
+		} else if (strcasecmp(val, "OSEN") == 0) {
+			dut->ap_key_mgmt = AP_OSEN;
+			dut->ap_cipher = AP_CCMP;
+		} else if (strcasecmp(val, "FT-EAP") == 0) {
+			dut->ap_key_mgmt = AP_WPA2_FT_EAP;
+			dut->ap_cipher = AP_CCMP;
+			dut->ap_pmf = AP_PMF_OPTIONAL;
+		} else if (strcasecmp(val, "FT-PSK") == 0) {
+			dut->ap_key_mgmt = AP_WPA2_FT_PSK;
+			dut->ap_cipher = AP_CCMP;
+			dut->ap_pmf = AP_PMF_OPTIONAL;
+		} else if (strcasecmp(val, "WPA2-ENT-256") == 0) {
+			dut->ap_key_mgmt = AP_WPA2_EAP_SHA256;
+			dut->ap_cipher = AP_CCMP;
+			dut->ap_pmf = AP_PMF_OPTIONAL;
+		} else if (strcasecmp(val, "WPA2-PSK-256") == 0) {
+			dut->ap_key_mgmt = AP_WPA2_PSK_SHA256;
+			dut->ap_cipher = AP_CCMP;
+			dut->ap_pmf = AP_PMF_OPTIONAL;
+		} else if (strcasecmp(val, "WPA2-ENT-FT-EAP") == 0) {
+			dut->ap_key_mgmt = AP_WPA2_ENT_FT_EAP;
+			dut->ap_cipher = AP_CCMP;
+			dut->ap_pmf = AP_PMF_OPTIONAL;
 		} else if (strcasecmp(val, "NONE") == 0) {
 			dut->ap_key_mgmt = AP_OPEN;
 			dut->ap_cipher = AP_PLAIN;
@@ -2098,10 +2130,10 @@
 }
 
 
-#define OPENWRT_MAX_NUM_RADIOS 3
+#define OPENWRT_MAX_NUM_RADIOS (MAX_RADIO + 1)
 static void owrt_ap_config_radio(struct sigma_dut *dut)
 {
-	int radio_id[MAX_RADIO] = { 0, 1 };
+	int radio_id[MAX_RADIO] = { 0, 1, 2 };
 	int radio_count, radio_no;
 	char buf[64];
 
@@ -2556,7 +2588,8 @@
 		}
 
 		if (strlen(dut->ap_osu_ssid)) {
-			if (strcmp(dut->ap_tag_ssid[0],
+			if (dut->ap_tag_ssid[0][0] &&
+			    strcmp(dut->ap_tag_ssid[0],
 				   dut->ap_osu_ssid) != 0 &&
 			    strcmp(dut->ap_tag_ssid[0], osu_ssid) != 0) {
 				sigma_dut_print(dut, DUT_MSG_ERROR,
@@ -2742,7 +2775,7 @@
 						"hidden", "1");
 			}
 
-			if (dut->ap_ft_oa == 1) {
+			if (ap_ft_enabled(dut)) {
 				unsigned char self_mac[ETH_ALEN];
 				char mac_str[20];
 
@@ -2849,7 +2882,7 @@
 			owrt_ap_set_list_vap(dut, vap_count, "anqp_elem",
 					     anqp_string);
 
-			if (dut->ap_ft_oa == 1) {
+			if (ap_ft_enabled(dut)) {
 				owrt_ap_set_vap(dut, vap_count,
 						"mobility_domain",
 						dut->ap_mobility_domain);
@@ -3100,6 +3133,12 @@
 			owrt_ap_set_vap(dut, vap_count, "auth_secret", buf);
 			break;
 		case AP_WPA2_EAP_OSEN:
+		case AP_OSEN:
+		case AP_WPA2_FT_EAP:
+		case AP_WPA2_FT_PSK:
+		case AP_WPA2_EAP_SHA256:
+		case AP_WPA2_PSK_SHA256:
+		case AP_WPA2_ENT_FT_EAP:
 			/* TODO */
 			break;
 		case AP_SUITEB:
@@ -3391,7 +3430,7 @@
 		owrt_ap_set_vap(dut, vap_id, "gas_comeback_delay", buf);
 	}
 
-	if (dut->ap_ft_oa == 1) {
+	if (ap_ft_enabled(dut)) {
 		unsigned char self_mac[ETH_ALEN];
 		char mac_str[20];
 
@@ -3425,13 +3464,13 @@
 		owrt_ap_set_vap(dut, vap_id, "ap2_r1_key_holder", mac_str);
 	}
 
-	if ((dut->ap_ft_oa == 1 && dut->ap_name == 0) ||
-	    (dut->ap_ft_oa == 1 && dut->ap_name == 2)) {
+	if ((ap_ft_enabled(dut) && dut->ap_name == 0) ||
+	    (ap_ft_enabled(dut) && dut->ap_name == 2)) {
 		owrt_ap_set_vap(dut, vap_id, "nasid2", "nas2.example.com");
 		owrt_ap_set_vap(dut, vap_id, "nasid", "nas1.example.com");
 	}
 
-	if (dut->ap_ft_oa == 1 && dut->ap_name == 1) {
+	if (ap_ft_enabled(dut) && dut->ap_name == 1) {
 		owrt_ap_set_vap(dut, vap_id, "nasid2", "nas1.example.com");
 		owrt_ap_set_vap(dut, vap_id, "nasid", "nas2.example.com");
 	}
@@ -3994,6 +4033,12 @@
 	case AP_SUITEB:
 	case AP_WPA2_OWE:
 	case AP_WPA2_EAP_OSEN:
+	case AP_OSEN:
+	case AP_WPA2_FT_EAP:
+	case AP_WPA2_FT_PSK:
+	case AP_WPA2_EAP_SHA256:
+	case AP_WPA2_PSK_SHA256:
+	case AP_WPA2_ENT_FT_EAP:
 		/* Not supported */
 		break;
 	}
@@ -4279,7 +4324,8 @@
 		}
 
 		if (strlen(dut->ap_osu_ssid)) {
-			if (strcmp(dut->ap_tag_ssid[0], dut->ap_osu_ssid) &&
+			if (dut->ap_tag_ssid[0][0] &&
+			    strcmp(dut->ap_tag_ssid[0], dut->ap_osu_ssid) &&
 			    strcmp(dut->ap_tag_ssid[0], osu_ssid)) {
 				sigma_dut_print(dut, DUT_MSG_ERROR,
 						"OSU_SSID and "
@@ -4356,9 +4402,6 @@
 	if (dut->ap_tnc_time_stamp)
 		fprintf(f, "hs20_t_c_timestamp=%u\n", dut->ap_tnc_time_stamp);
 
-	if (dut->ap_tnc_url)
-		fprintf(f, "hs20_t_c_server_url=%s\n", dut->ap_tnc_url);
-
 	return 0;
 }
 
@@ -5933,6 +5976,16 @@
 		/* TODO */
 		sigma_dut_print(dut, DUT_MSG_ERROR, "OWE not supported");
 		break;
+	case AP_WPA2_FT_EAP:
+	case AP_WPA2_FT_PSK:
+	case AP_WPA2_EAP_SHA256:
+	case AP_WPA2_PSK_SHA256:
+	case AP_WPA2_ENT_FT_EAP:
+	case AP_OSEN:
+		/* TODO */
+		send_resp(dut, conn, SIGMA_ERROR,
+			  "errorCode,Unsupported KeyMgnt value");
+		return 0;
 	}
 
 	if (dut->ap_is_dual) {
@@ -6031,6 +6084,16 @@
 			sigma_dut_print(dut, DUT_MSG_ERROR,
 					"OWE not supported");
 			break;
+		case AP_WPA2_FT_EAP:
+		case AP_WPA2_FT_PSK:
+		case AP_WPA2_EAP_SHA256:
+		case AP_WPA2_PSK_SHA256:
+		case AP_WPA2_ENT_FT_EAP:
+		case AP_OSEN:
+			/* TODO */
+			send_resp(dut, conn, SIGMA_ERROR,
+				  "errorCode,Unsupported KeyMgnt value");
+			return 0;
 		}
 
 		/* wifi0 settings in case of dual */
@@ -6605,9 +6668,13 @@
 	case AP_WPA_PSK:
 	case AP_WPA2_SAE:
 	case AP_WPA2_PSK_SAE:
+	case AP_WPA2_PSK_SHA256:
+	case AP_WPA2_FT_PSK:
 		if (dut->ap_key_mgmt == AP_WPA2_PSK ||
 		    dut->ap_key_mgmt == AP_WPA2_SAE ||
-		    dut->ap_key_mgmt == AP_WPA2_PSK_SAE)
+		    dut->ap_key_mgmt == AP_WPA2_PSK_SAE ||
+		    dut->ap_key_mgmt == AP_WPA2_PSK_SHA256 ||
+		    dut->ap_key_mgmt == AP_WPA2_FT_PSK)
 			fprintf(f, "wpa=2\n");
 		else if (dut->ap_key_mgmt == AP_WPA2_PSK_MIXED)
 			fprintf(f, "wpa=3\n");
@@ -6638,6 +6705,10 @@
 			fprintf(f, "wpa_key_mgmt=%s\n", key_mgmt);
 			break;
 		}
+		if (dut->ap_key_mgmt == AP_WPA2_PSK_SHA256)
+			fprintf(f, "wpa_key_mgmt=WPA-PSK-SHA256\n");
+		else if (dut->ap_key_mgmt == AP_WPA2_FT_PSK)
+			fprintf(f, "wpa_key_mgmt=FT-PSK\n");
 		fprintf(f, "wpa_pairwise=%s\n",
 			hostapd_cipher_name(dut->ap_cipher));
 		if (dut->ap_group_cipher != AP_NO_GROUP_CIPHER_SET)
@@ -6654,9 +6725,15 @@
 	case AP_WPA2_EAP_MIXED:
 	case AP_WPA_EAP:
 	case AP_WPA2_EAP_OSEN:
+	case AP_WPA2_EAP_SHA256:
+	case AP_WPA2_FT_EAP:
+	case AP_WPA2_ENT_FT_EAP:
 		fprintf(f, "ieee8021x=1\n");
 		if (dut->ap_key_mgmt == AP_WPA2_EAP ||
-		    dut->ap_key_mgmt == AP_WPA2_EAP_OSEN)
+		    dut->ap_key_mgmt == AP_WPA2_EAP_OSEN ||
+		    dut->ap_key_mgmt == AP_WPA2_EAP_SHA256 ||
+		    dut->ap_key_mgmt == AP_WPA2_FT_EAP ||
+		    dut->ap_key_mgmt == AP_WPA2_ENT_FT_EAP)
 			fprintf(f, "wpa=2\n");
 		else if (dut->ap_key_mgmt == AP_WPA2_EAP_MIXED)
 			fprintf(f, "wpa=3\n");
@@ -6679,6 +6756,12 @@
 				"");
 			break;
 		}
+		if (dut->ap_key_mgmt == AP_WPA2_EAP_SHA256)
+			fprintf(f, "wpa_key_mgmt=WPA-EAP-SHA256\n");
+		else if (dut->ap_key_mgmt == AP_WPA2_FT_EAP)
+			fprintf(f, "wpa_key_mgmt=FT-EAP\n");
+		else if (dut->ap_key_mgmt == AP_WPA2_ENT_FT_EAP)
+			fprintf(f, "wpa_key_mgmt=FT-EAP WPA-EAP\n");
 		fprintf(f, "wpa_pairwise=%s\n",
 			hostapd_cipher_name(dut->ap_cipher));
 		if (dut->ap_group_cipher != AP_NO_GROUP_CIPHER_SET)
@@ -6690,6 +6773,12 @@
 				dut->ap_radius_port);
 		fprintf(f, "auth_server_shared_secret=%s\n",
 			dut->ap_radius_password);
+		if (dut->program == PROGRAM_HS2_R3) {
+			fprintf(f, "radius_das_port=3799\n");
+			fprintf(f, "radius_das_client=0.0.0.0 %s\n",
+				dut->ap_radius_password);
+			fprintf(f, "radius_das_require_event_timestamp=1\n");
+		}
 		break;
 	case AP_SUITEB:
 		fprintf(f, "ieee8021x=1\n");
@@ -6719,6 +6808,21 @@
 		if (dut->ap_sae_groups)
 			fprintf(f, "owe_groups=%s\n", dut->ap_sae_groups);
 		break;
+	case AP_OSEN:
+		fprintf(f, "osen=1\n");
+		fprintf(f, "disable_dgaf=1\n");
+		fprintf(f, "wpa_pairwise=%s\n",
+			hostapd_cipher_name(dut->ap_cipher));
+		if (dut->ap_group_cipher != AP_NO_GROUP_CIPHER_SET)
+			fprintf(f, "group_cipher=%s\n",
+				hostapd_cipher_name(dut->ap_group_cipher));
+		fprintf(f, "auth_server_addr=%s\n", dut->ap_radius_ipaddr);
+		if (dut->ap_radius_port)
+			fprintf(f, "auth_server_port=%d\n",
+				dut->ap_radius_port);
+		fprintf(f, "auth_server_shared_secret=%s\n",
+			dut->ap_radius_password);
+		break;
 	}
 
 	if (dut->ap_rsn_preauth)
@@ -6737,6 +6841,27 @@
 		break;
 	}
 
+	if (ap_ft_enabled(dut)) {
+		unsigned char own_addr[ETH_ALEN];
+
+		fprintf(f, "mobility_domain=%s\n", dut->ap_mobility_domain);
+		fprintf(f, "ft_over_ds=0\n");
+		fprintf(f, "nas_identifier=nas1.example.com\n");
+		if (get_hwaddr(ifname, own_addr) < 0) {
+			memset(own_addr, 0, ETH_ALEN);
+			own_addr[0] = 0x02;
+		}
+		fprintf(f, "r1_key_holder=%02x%02x%02x%02x%02x%02x\n",
+			own_addr[0], own_addr[1], own_addr[2],
+			own_addr[3], own_addr[4], own_addr[5]);
+		fprintf(f, "ft_psk_generate_local=1\n");
+		fprintf(f, "pmk_r1_push=0\n");
+		fprintf(f,
+			"r0kh=ff:ff:ff:ff:ff:ff * 00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff");
+		fprintf(f,
+			"r1kh=00:00:00:00:00:00 00:00:00:00:00:00 00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff");
+	}
+
 	if (dut->rsne_override)
 		fprintf(f, "own_ie_override=%s\n", dut->rsne_override);
 
@@ -7526,10 +7651,9 @@
 	dut->ap_oper_icon_metadata = 0;
 	dut->ap_tnc_file_name = 0;
 	dut->ap_tnc_time_stamp = 0;
-	free(dut->ap_tnc_url);
-	dut->ap_tnc_url = NULL;
 
 	if (dut->program == PROGRAM_HS2 || dut->program == PROGRAM_HS2_R2 ||
+	    dut->program == PROGRAM_HS2_R3 ||
 	    dut->program == PROGRAM_IOTLP) {
 		int i;
 
@@ -7586,7 +7710,8 @@
 		dut->ap_add_sha256 = 0;
 	}
 
-	if (dut->program == PROGRAM_HS2_R2 || dut->program == PROGRAM_IOTLP) {
+	if (dut->program == PROGRAM_HS2_R2 || dut->program == PROGRAM_HS2_R3 ||
+	    dut->program == PROGRAM_IOTLP) {
 		int i;
 		const char hessid[] = "50:6f:9a:00:11:22";
 
@@ -9347,12 +9472,6 @@
 	if (val)
 		dut->ap_tnc_time_stamp = strtol(val, NULL, 10);
 
-	val = get_param(cmd, "TnC_URL");
-	if (val) {
-		free(dut->ap_tnc_url);
-		dut->ap_tnc_url = strdup(val);
-	}
-
 	return 1;
 }
 
diff --git a/dev.c b/dev.c
index 4b6f833..2c8bfc9 100644
--- a/dev.c
+++ b/dev.c
@@ -1,12 +1,16 @@
 /*
  * Sigma Control API DUT (station/AP/sniffer)
  * Copyright (c) 2011-2013, 2017, Qualcomm Atheros, Inc.
+ * Copyright (c) 2018, The Linux Foundation
  * All Rights Reserved.
  * Licensed under the Clear BSD license. See README for more details.
  */
 
 #include "sigma_dut.h"
 #include "miracast.h"
+#include <sys/wait.h>
+#include "wpa_ctrl.h"
+#include "wpa_helpers.h"
 
 
 static int cmd_dev_send_frame(struct sigma_dut *dut, struct sigma_conn *conn,
@@ -103,12 +107,134 @@
 }
 
 
+static int cmd_dev_ble_action(struct sigma_dut *dut, struct sigma_conn *conn,
+			      struct sigma_cmd *cmd)
+{
+#ifdef ANDROID
+	char buf[200];
+	const char *ble_op = get_param(cmd, "BLEOp");
+	const char *prog = get_param(cmd, "Prog");
+	const char *service_name = get_param(cmd, "ServiceName");
+	const char *ble_role = get_param(cmd, "BLERole");
+	const char *discovery_type = get_param(cmd, "DiscoveryType");
+	const char *msg_type = get_param(cmd, "messagetype");
+	const char *action = get_param(cmd, "action");
+	const char *M2Transmit = get_param(cmd, "M2Transmit");
+	char *argv[17];
+	pid_t pid;
+
+	if (prog && ble_role && action && msg_type) {
+		send_resp(dut, conn, SIGMA_COMPLETE,
+			  "OrgID,0x00,TransDataHeader,0x00,BloomFilterElement,NULL");
+		return 0;
+	}
+	if (!ble_op || !prog || !service_name || !ble_role || !discovery_type) {
+		sigma_dut_print(dut, DUT_MSG_ERROR, "Invalid arguments");
+		return -1;
+	}
+
+	if ((strcasecmp(prog, "NAN") != 0)) {
+		sigma_dut_print(dut, DUT_MSG_ERROR, "Program %s not supported",
+				prog);
+		return -1;
+	}
+
+	if (strcasecmp(ble_role, "seeker") != 0 &&
+	    strcasecmp(ble_role, "provider") != 0 &&
+	    strcasecmp(ble_role, "browser") != 0) {
+		sigma_dut_print(dut, DUT_MSG_ERROR, "Invalid BLERole: %s",
+				ble_role);
+		return -1;
+	}
+
+	if (strcasecmp(discovery_type, "active") != 0 &&
+	    strcasecmp(discovery_type, "passive") != 0) {
+		sigma_dut_print(dut, DUT_MSG_ERROR, "Invalid DiscoveryType: %s",
+				discovery_type);
+		return -1;
+	}
+
+	if (!M2Transmit)
+		M2Transmit = "disable";
+
+	argv[0] = "am";
+	argv[1] = "start";
+	argv[2] = "-n";
+	argv[3] = "org.codeaurora.nanservicediscovery/org.codeaurora.nanservicediscovery.MainActivity";
+	argv[4] = "--es";
+	argv[5] = "service";
+	argv[6] = (char *) service_name;
+	argv[7] = "--es";
+	argv[8] = "role";
+	argv[9] = (char *) ble_role;
+	argv[10] = "--es";
+	argv[11] = "scantype";
+	argv[12] = (char *) discovery_type;
+	argv[13] = "--es";
+	argv[14] = "M2Transmit";
+	argv[15] = (char *) M2Transmit;
+	argv[16] = NULL;
+
+	pid = fork();
+	if (pid == -1) {
+		sigma_dut_print(dut, DUT_MSG_ERROR, "fork: %s",
+				strerror(errno));
+		return -1;
+	}
+
+	if (pid == 0) {
+		execv("/system/bin/am", argv);
+		sigma_dut_print(dut, DUT_MSG_ERROR, "execv: %s",
+				strerror(errno));
+		exit(0);
+		return -1;
+	}
+
+	dut->nanservicediscoveryinprogress = 1;
+#endif /* ANDROID */
+
+	return 1;
+}
+
+
+
+static int cmd_dev_start_test(struct sigma_dut *dut, struct sigma_conn *conn,
+			      struct sigma_cmd *cmd)
+{
+	return 1;
+}
+
+
+static int cmd_dev_stop_test(struct sigma_dut *dut, struct sigma_conn *conn,
+			     struct sigma_cmd *cmd)
+{
+	return 1;
+}
+
+
+static int cmd_dev_get_log(struct sigma_dut *dut, struct sigma_conn *conn,
+			   struct sigma_cmd *cmd)
+{
+	return 1;
+}
+
+
 static int req_intf(struct sigma_cmd *cmd)
 {
 	return get_param(cmd, "interface") == NULL ? -1 : 0;
 }
 
 
+static int req_role_svcname(struct sigma_cmd *cmd)
+{
+	if (!get_param(cmd, "BLERole"))
+		 return -1;
+	if (get_param(cmd, "BLEOp") && !get_param(cmd, "ServiceName"))
+		return -1;
+	return 0;
+}
+
+
 static int req_intf_prog(struct sigma_cmd *cmd)
 {
 	if (get_param(cmd, "interface") == NULL)
@@ -135,4 +261,9 @@
 	sigma_dut_reg_cmd("dev_exec_action", req_prog,
 			  cmd_dev_exec_action);
 	sigma_dut_reg_cmd("dev_configure_ie", req_intf, cmd_dev_configure_ie);
+	sigma_dut_reg_cmd("dev_start_test", NULL, cmd_dev_start_test);
+	sigma_dut_reg_cmd("dev_stop_test", NULL, cmd_dev_stop_test);
+	sigma_dut_reg_cmd("dev_get_log", NULL, cmd_dev_get_log);
+	sigma_dut_reg_cmd("dev_ble_action", req_role_svcname,
+			  cmd_dev_ble_action);
 }
diff --git a/dpp.c b/dpp.c
index 3f7ea6f..001e0b8 100644
--- a/dpp.c
+++ b/dpp.c
@@ -895,7 +895,8 @@
 		"CTRL-EVENT-CONNECTED",
 		NULL
 	};
-	const char *groups_override = NULL;
+	const char *group_id_str = NULL;
+	char group_id[100];
 	const char *result;
 	int check_mutual = 0;
 	int enrollee_ap;
@@ -1037,6 +1038,7 @@
 
 	conf_ssid[0] = '\0';
 	conf_pass[0] = '\0';
+	group_id[0] = '\0';
 	val = get_param(cmd, "DPPConfIndex");
 	if (val)
 		conf_index = atoi(val);
@@ -1049,11 +1051,10 @@
 		snprintf(conf_ssid, sizeof(conf_ssid), "ssid=%s", buf);
 		if (enrollee_ap) {
 			conf_role = "ap-dpp";
-			groups_override = "[{\"groupId\":\"DPPGROUP_DPP_INFRA\",\"netRole\":\"ap\"}]";
 		} else {
 			conf_role = "sta-dpp";
-			groups_override = "[{\"groupId\":\"DPPGROUP_DPP_INFRA\",\"netRole\":\"sta\"}]";
 		}
+		group_id_str = "DPPGROUP_DPP_INFRA";
 		break;
 	case 2:
 		ascii2hexstr("DPPNET01", buf);
@@ -1080,11 +1081,10 @@
 		snprintf(conf_ssid, sizeof(conf_ssid), "ssid=%s", buf);
 		if (enrollee_ap) {
 			conf_role = "ap-dpp";
-			groups_override = "[{\"groupId\":\"DPPGROUP_DPP_INFRA2\",\"netRole\":\"ap\"}]";
 		} else {
 			conf_role = "sta-dpp";
-			groups_override = "[{\"groupId\":\"DPPGROUP_DPP_INFRA2\",\"netRole\":\"sta\"}]";
 		}
+		group_id_str = "DPPGROUP_DPP_INFRA2";
 		break;
 	case 5:
 		ascii2hexstr("DPPNET01", buf);
@@ -1111,11 +1111,10 @@
 		snprintf(conf_ssid, sizeof(conf_ssid), "ssid=%s", buf);
 		if (enrollee_ap) {
 			conf_role = "ap-dpp";
-			groups_override = "[{\"groupId\":\"DPPGROUP_DPP_INFRA\",\"netRole\":\"ap\"}]";
 		} else {
 			conf_role = "sta-dpp";
-			groups_override = "[{\"groupId\":\"DPPGROUP_DPP_INFRA\",\"netRole\":\"sta\"}]";
 		}
+		group_id_str = "DPPGROUP_DPP_INFRA";
 		force_gas_fragm = 1;
 		break;
 	default:
@@ -1124,15 +1123,9 @@
 		goto out;
 	}
 
-	if (groups_override) {
-		snprintf(buf, sizeof(buf), "SET dpp_groups_override %s",
-			 groups_override);
-		if (wpa_command(ifname, buf) < 0) {
-			send_resp(dut, conn, SIGMA_ERROR,
-				  "errorCode,Failed to set cred:groups");
-			goto out;
-		}
-	}
+	if (group_id_str)
+		snprintf(group_id, sizeof(group_id), " group_id=%s",
+			 group_id_str);
 
 	if (force_gas_fragm) {
 		char spaces[1500];
@@ -1261,14 +1254,15 @@
 				goto out;
 			}
 			snprintf(buf, sizeof(buf),
-				 "DPP_AUTH_INIT peer=%d%s role=%s conf=%s %s %s configurator=%d%s",
+				 "DPP_AUTH_INIT peer=%d%s role=%s conf=%s %s %s configurator=%d%s%s",
 				 dpp_peer_bootstrap, own_txt, role,
 				 conf_role, conf_ssid, conf_pass,
-				 dut->dpp_conf_id, neg_freq);
+				 dut->dpp_conf_id, neg_freq, group_id);
 		} else if (strcasecmp(bs, "QR") == 0) {
 			snprintf(buf, sizeof(buf),
-				 "DPP_AUTH_INIT peer=%d%s role=%s%s",
-				 dpp_peer_bootstrap, own_txt, role, neg_freq);
+				 "DPP_AUTH_INIT peer=%d%s role=%s%s%s",
+				 dpp_peer_bootstrap, own_txt, role,
+				 neg_freq, group_id);
 		} else if (strcasecmp(bs, "PKEX") == 0 &&
 			   (strcasecmp(prov_role, "Configurator") == 0 ||
 			    strcasecmp(prov_role, "Both") == 0)) {
@@ -1339,9 +1333,9 @@
 				goto out;
 			}
 			snprintf(buf, sizeof(buf),
-				 "SET dpp_configurator_params  conf=%s %s %s configurator=%d",
+				 "SET dpp_configurator_params  conf=%s %s %s configurator=%d%s",
 				 conf_role, conf_ssid, conf_pass,
-				 dut->dpp_conf_id);
+				 dut->dpp_conf_id, group_id);
 			if (wpa_command(ifname, buf) < 0) {
 				send_resp(dut, conn, SIGMA_ERROR,
 					  "errorCode,Failed to set configurator parameters");
diff --git a/nan.c b/nan.c
index 4a7d0b9..18f23c6 100644
--- a/nan.c
+++ b/nan.c
@@ -1889,6 +1889,31 @@
 {
 	sigma_dut_print(dut, DUT_MSG_INFO, "NAN sta_reset_default");
 
+#ifdef ANDROID
+	if (dut->nanservicediscoveryinprogress) {
+		char *argv[5];
+		pid_t pid;
+
+		argv[0] = "am";
+		argv[1] = "broadcast";
+		argv[2] = "-a";
+		argv[3] = "org.codeaurora.nanservicediscovery.close";
+		argv[4] = NULL;
+
+		pid = fork();
+		if (pid == -1) {
+			sigma_dut_print(dut, DUT_MSG_ERROR, "fork: %s",
+					strerror(errno));
+		} else if (pid == 0) {
+			execv("/system/bin/am", argv);
+			sigma_dut_print(dut, DUT_MSG_ERROR, "execv: %s",
+					strerror(errno));
+			exit(0);
+		}
+		dut->nanservicediscoveryinprogress = 0;
+	}
+#endif /* ANDROID */
+
 	if (nan_state == 0) {
 		nan_init(dut);
 		nan_state = 1;
@@ -1922,6 +1947,7 @@
 	const char *nan_op = get_param(cmd, "NANOp");
 	const char *method_type = get_param(cmd, "MethodType");
 	const char *band = get_param(cmd, "band");
+	const char *disc_mac_addr = get_param(cmd, "DiscoveryMacAddress");
 	char resp_buf[100];
 	wifi_error ret;
 
@@ -2055,6 +2081,12 @@
 				sigma_nan_schedule_update(dut, cmd);
 				send_resp(dut, conn, SIGMA_COMPLETE, "NULL");
 			}
+		} else if (disc_mac_addr &&
+			   strcasecmp(disc_mac_addr, "GET") == 0) {
+			snprintf(resp_buf, sizeof(resp_buf), "mac,"
+				 MAC_ADDR_STR,
+				 MAC_ADDR_ARRAY(global_nan_mac_addr));
+			send_resp(dut, conn, SIGMA_COMPLETE, resp_buf);
 		} else {
 			sigma_nan_config_enable(dut, conn, cmd);
 			snprintf(resp_buf, sizeof(resp_buf), "mac,"
diff --git a/qca-vendor_copy.h b/qca-vendor_copy.h
index ed8fb3a..e89f276 100644
--- a/qca-vendor_copy.h
+++ b/qca-vendor_copy.h
@@ -3516,6 +3516,14 @@
 
 	QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_BUCKETS_SCANNED = 45,
 
+	/* Unsigned 32-bit value; a GSCAN Capabilities attribute.
+	 * This is used to limit the maximum number of BSSIDs while sending
+	 * the vendor command QCA_NL80211_VENDOR_SUBCMD_ROAM with attributes
+	 * QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_SET_BLACKLIST_BSSID and
+	 * QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_NUM_BSSID.
+	 */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_MAX_NUM_BLACKLISTED_BSSID = 46,
+
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_AFTER_LAST,
 	QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_MAX =
@@ -5356,6 +5364,24 @@
 	QCA_WLAN_HE_LTF_4X = 3,
 };
 
+/**
+ * enum qca_wlan_he_mac_padding_dur - HE trigger frame MAC padding duration
+ *
+ * Indicates the HE trigger frame MAC padding duration value.
+ *
+ * @QCA_WLAN_HE_NO_ADDITIONAL_PROCESS_TIME: no additional time required to
+ * process the trigger frame.
+ * @QCA_WLAN_HE_8US_OF_PROCESS_TIME: indicates the 8us of processing time for
+ * trigger frame.
+ * @QCA_WLAN_HE_16US_OF_PROCESS_TIME: indicates the 16us of processing time for
+ * trigger frame.
+ */
+enum qca_wlan_he_mac_padding_dur {
+	QCA_WLAN_HE_NO_ADDITIONAL_PROCESS_TIME = 0,
+	QCA_WLAN_HE_8US_OF_PROCESS_TIME = 1,
+	QCA_WLAN_HE_16US_OF_PROCESS_TIME = 2,
+};
+
 /* Attributes for data used by
  * QCA_NL80211_VENDOR_SUBCMD_WIFI_TEST_CONFIGURATION
  */
@@ -5416,10 +5442,10 @@
 	 */
 	QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ADD_DEL_BA_SESSION = 7,
 
-	/* 8-bit unsigned value to configure the buffer size in addba
+	/* 16-bit unsigned value to configure the buffer size in addba
 	 * request and response frames.
 	 * This attribute is used to configure the testbed device.
-	 * The range of the value is 0 to 255.
+	 * The range of the value is 0 to 256.
 	 */
 	QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ADDBA_BUFF_SIZE = 8,
 
@@ -5455,6 +5481,64 @@
 	 */
 	QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ENABLE_TX_BEAMFORMEE = 13,
 
+	/* 8-bit unsigned value to configure the tx beamformee number
+	 * of space-time streams.
+	 * This attribute is used to configure the testbed device.
+	 * The range of the value is 0 to 8.
+	 */
+	QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_TX_BEAMFORMEE_NSTS = 14,
+
+	/* 8-bit unsigned value to configure the MU EDCA params for given AC
+	 * This attribute is used to configure the testbed device.
+	 * Uses the enum qca_wlan_ac_type values.
+	 */
+	QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_MU_EDCA_AC = 15,
+
+	/* 8-bit unsigned value to configure the MU EDCA AIFSN for given AC
+	 * To configure MU EDCA AIFSN value, MU EDCA access category value
+	 * is required to process the command.
+	 * This attribute is used to configure the testbed device.
+	 */
+	QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_MU_EDCA_AIFSN = 16,
+
+	/* 8-bit unsigned value to configure the MU EDCA ECW min value for
+	 * given AC.
+	 * To configure MU EDCA ECW min value, MU EDCA access category value
+	 * is required to process the command.
+	 * This attribute is used to configure the testbed device.
+	 */
+	QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_MU_EDCA_ECWMIN = 17,
+
+	/* 8-bit unsigned value to configure the MU EDCA ECW max value for
+	 * given AC.
+	 * To configure MU EDCA ECW max value, MU EDCA access category value
+	 * is required to process the command.
+	 * This attribute is used to configure the testbed device.
+	 */
+	QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_MU_EDCA_ECWMAX = 18,
+
+	/* 8-bit unsigned value to configure the MU EDCA timer for given AC
+	 * To configure MU EDCA timer value, MU EDCA access category value
+	 * is required to process the command.
+	 * This attribute is used to configure the testbed device.
+	 */
+	QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_MU_EDCA_TIMER = 19,
+
+	/* 8-bit unsigned value to configure the HE trigger frame MAC padding
+	 * duration.
+	 * This attribute is used to configure the testbed device.
+	 * Uses the enum qca_wlan_he_mac_padding_dur values.
+	 */
+	QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_MAC_PADDING_DUR = 20,
+
+	/* 8-bit unsigned value to override the MU EDCA params to defaults
+	 * regardless of the AP beacon MU EDCA params. If it is enabled use
+	 * the default values else use the MU EDCA params from AP beacon.
+	 * This attribute is used to configure the testbed device.
+	 * 1-enable, 0-disable.
+	 */
+	QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_OVERRIDE_MU_EDCA = 21,
+
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_AFTER_LAST,
 	QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_MAX =
diff --git a/server.c b/server.c
index 17fffb4..7031fba 100644
--- a/server.c
+++ b/server.c
@@ -1,11 +1,178 @@
 /*
  * Sigma Control API DUT (server)
  * Copyright (c) 2014, Qualcomm Atheros, Inc.
+ * Copyright (c) 2018, The Linux Foundation
  * All Rights Reserved.
  * Licensed under the Clear BSD license. See README for more details.
  */
 
 #include "sigma_dut.h"
+#include <sqlite3.h>
+
+#ifndef SERVER_DB
+#define SERVER_DB "/home/user/hs20-server/AS/DB/eap_user.db"
+#endif /* SERVER_DB */
+
+#ifndef CERT_DIR
+#define CERT_DIR "/home/user/hs20-server/certs"
+#endif /* CERT_DIR */
+
+
+static int cmd_server_ca_get_version(struct sigma_dut *dut,
+				     struct sigma_conn *conn,
+				     struct sigma_cmd *cmd)
+{
+	send_resp(dut, conn, SIGMA_COMPLETE, "version,1.0");
+	return 0;
+}
+
+
+static int cmd_server_get_info(struct sigma_dut *dut,
+			       struct sigma_conn *conn,
+			       struct sigma_cmd *cmd)
+{
+	send_resp(dut, conn, SIGMA_COMPLETE, "vendor,OSU,model,OS,version,1.0");
+	return 0;
+}
+
+
+static int server_reset_user(struct sigma_dut *dut, const char *user)
+{
+	sqlite3 *db;
+	int res = -1;
+	char *sql = NULL;
+	const char *realm = "wi-fi.org";
+	const char *methods = "TTLS-MSCHAPV2";
+	const char *password = "ChangeMe";
+	int phase2 = 1;
+	int machine_managed = 1;
+	int remediation = 0;
+	int fetch_pps = 0;
+	const char *osu_user = NULL;
+	const char *osu_password = NULL;
+
+	sigma_dut_print(dut, DUT_MSG_DEBUG, "Reset user %s", user);
+
+	if (sqlite3_open(SERVER_DB, &db)) {
+		sigma_dut_print(dut, DUT_MSG_ERROR,
+				"Failed to open SQLite database %s",
+				SERVER_DB);
+		return -1;
+	}
+
+	if (strcmp(user, "test01") == 0) {
+	} else if (strcmp(user, "test02") == 0) {
+		machine_managed = 0;
+	} else if (strcmp(user, "test03") == 0) {
+	} else if (strcmp(user, "test04") == 0) {
+	} else if (strcmp(user, "test05") == 0) {
+	} else if (strcmp(user, "test06") == 0) {
+		realm = "example.com";
+	} else if (strcmp(user, "test07") == 0) {
+	} else if (strcmp(user, "test08") == 0) {
+		osu_user = "testdmacc08";
+		osu_password = "P@ssw0rd";
+	} else if (strcmp(user, "test09") == 0) {
+	} else if (strcmp(user, "test10") == 0) {
+		methods = "TLS";
+	} else if (strcmp(user, "test11") == 0) {
+	} else if (strcmp(user, "test12") == 0) {
+		methods = "TLS";
+	} else if (strcmp(user, "test20") == 0) {
+	} else if (strcmp(user, "test26") == 0) {
+		/* TODO: Cred01 with username/password? */
+		user = "1310026000000001";
+		methods = "SIM";
+	} else if (strcmp(user, "test30") == 0) {
+		osu_user = "testdmacc30";
+		osu_password = "P@ssw0rd";
+	} else if (strcmp(user, "test31") == 0) {
+		osu_user = "testdmacc31";
+		osu_password = "P@ssw0rd";
+	} else if (strcmp(user, "test32") == 0) {
+		osu_user = "testdmacc32";
+		osu_password = "P@ssw0rd";
+	} else if (strcmp(user, "test33") == 0) {
+		osu_user = "testdmacc33";
+		osu_password = "P@ssw0rd";
+	} else if (strcmp(user, "test34") == 0) {
+		osu_user = "testdmacc34";
+		osu_password = "P@ssw0rd";
+	} else if (strcmp(user, "test35") == 0) {
+		osu_user = "testdmacc35";
+		osu_password = "P@ssw0rd";
+	} else if (strcmp(user, "test36") == 0) {
+	} else if (strcmp(user, "test37") == 0) {
+		osu_user = "testdmacc37";
+		osu_password = "P@ssw0rd";
+	} else {
+		sigma_dut_print(dut, DUT_MSG_INFO, "Unsupported username '%s'",
+				user);
+		goto fail;
+	}
+
+	sql = sqlite3_mprintf("INSERT OR REPLACE INTO users(identity,realm,methods,password,phase2,machine_managed,remediation,fetch_pps,osu_user,osu_password) VALUES (%Q,%Q,%Q,%Q,%d,%d,%d,%d,%Q,%Q)",
+			      user, realm, methods, password,
+			      phase2, machine_managed, remediation, fetch_pps,
+			      osu_user, osu_password);
+
+	if (!sql)
+		goto fail;
+
+	sigma_dut_print(dut, DUT_MSG_DEBUG, "SQL: %s", sql);
+
+	if (sqlite3_exec(db, sql, NULL, NULL, NULL) != SQLITE_OK) {
+		sigma_dut_print(dut, DUT_MSG_ERROR, "SQL operation failed: %s",
+				sqlite3_errmsg(db));
+	} else {
+		res = 0;
+	}
+
+	sqlite3_free(sql);
+
+fail:
+	sqlite3_close(db);
+
+	return res;
+}
+
+
+static int server_reset_cert_enroll(struct sigma_dut *dut, const char *addr)
+{
+	sqlite3 *db;
+	char *sql;
+
+	sigma_dut_print(dut, DUT_MSG_DEBUG,
+			"Reset certificate enrollment status for %s", addr);
+
+	if (sqlite3_open(SERVER_DB, &db)) {
+		sigma_dut_print(dut, DUT_MSG_ERROR,
+				"Failed to open SQLite database %s",
+				SERVER_DB);
+		return -1;
+	}
+	sql = sqlite3_mprintf("DELETE FROM cert_enroll WHERE mac_addr=%Q",
+			      addr);
+	if (!sql) {
+		sqlite3_close(db);
+		return -1;
+	}
+	sigma_dut_print(dut, DUT_MSG_DEBUG, "SQL: %s", sql);
+
+	if (sqlite3_exec(db, sql, NULL, NULL, NULL) != SQLITE_OK) {
+		sigma_dut_print(dut, DUT_MSG_ERROR,
+				"SQL operation failed: %s",
+				sqlite3_errmsg(db));
+		sqlite3_free(sql);
+		sqlite3_close(db);
+		return -1;
+	}
+
+	sqlite3_free(sql);
+	sqlite3_close(db);
+
+	return 0;
+}
 
 
 static int cmd_server_reset_default(struct sigma_dut *dut,
@@ -13,18 +180,27 @@
 				    struct sigma_cmd *cmd)
 {
 	const char *var;
+	enum sigma_program prog;
 
 	var = get_param(cmd, "Program");
-	if (var == NULL || strcasecmp(var, "HS2-R2") != 0) {
+	if (!var) {
+		send_resp(dut, conn, SIGMA_ERROR,
+			  "errorCode,Missing program parameter");
+		return 0;
+	}
+
+	prog = sigma_program_to_enum(var);
+	if (prog != PROGRAM_HS2_R2 && prog != PROGRAM_HS2_R3) {
 		send_resp(dut, conn, SIGMA_ERROR,
 			  "errorCode,Unsupported program");
 		return 0;
 	}
 
 	var = get_param(cmd, "UserName");
-	if (var) {
-		sigma_dut_print(dut, DUT_MSG_DEBUG, "Reset user %s", var);
-		/* TODO */
+	if (var && server_reset_user(dut, var) < 0) {
+		send_resp(dut, conn, SIGMA_ERROR,
+			  "errorCode,Failed to reset user account to defaults");
+		return 0;
 	}
 
 	var = get_param(cmd, "SerialNo");
@@ -34,10 +210,201 @@
 		/* TODO */
 	}
 
+	var = get_param(cmd, "ClientMACAddr");
+	if (var && server_reset_cert_enroll(dut, var) < 0) {
+		send_resp(dut, conn, SIGMA_ERROR,
+			  "errorCode,Failed to reset cert enroll to defaults");
+		return 0;
+	}
+
 	return 1;
 }
 
 
+static int get_last_msk_cb(void *ctx, int argc, char *argv[], char *col[])
+{
+	char **last_msk = ctx;
+
+	if (argc < 1 || !argv[0])
+		return 0;
+
+	free(*last_msk);
+	*last_msk = strdup(argv[0]);
+
+	return 0;
+}
+
+
+static char * get_last_msk(struct sigma_dut *dut, sqlite3 *db,
+			   const char *username)
+{
+	char *sql, *last_msk = NULL;
+
+	sql = sqlite3_mprintf("SELECT last_msk FROM users WHERE identity=%Q",
+			      username);
+	if (!sql)
+		return NULL;
+
+	if (sqlite3_exec(db, sql, get_last_msk_cb, &last_msk, NULL) !=
+	    SQLITE_OK) {
+		sigma_dut_print(dut, DUT_MSG_ERROR,
+				"SQL operation to fetch last_msk failed: %s",
+				sqlite3_errmsg(db));
+		sqlite3_free(sql);
+		return NULL;
+	}
+
+	sqlite3_free(sql);
+
+	return last_msk;
+}
+
+
+static int aaa_auth_status(struct sigma_dut *dut, struct sigma_conn *conn,
+			   struct sigma_cmd *cmd, const char *username,
+			   int timeout)
+{
+	sqlite3 *db;
+	char *sql = NULL;
+	int i;
+	char resp[500];
+
+	if (sqlite3_open(SERVER_DB, &db)) {
+		sigma_dut_print(dut, DUT_MSG_ERROR,
+				"Failed to open SQLite database %s",
+				SERVER_DB);
+		return -1;
+	}
+
+	sql = sqlite3_mprintf("UPDATE users SET last_msk=NULL WHERE identity=%Q",
+			      username);
+	if (!sql) {
+		sqlite3_close(db);
+		return -1;
+	}
+
+	if (sqlite3_exec(db, sql, NULL, NULL, NULL) != SQLITE_OK) {
+		sigma_dut_print(dut, DUT_MSG_ERROR,
+				"SQL operation to clear last_msk failed: %s",
+				sqlite3_errmsg(db));
+		sqlite3_free(sql);
+		sqlite3_close(db);
+		return -1;
+	}
+
+	sqlite3_free(sql);
+
+	snprintf(resp, sizeof(resp), "AuthStatus,TIMEOUT,MSK,NULL");
+
+	for (i = 0; i < timeout; i++) {
+		char *last_msk;
+
+		last_msk = get_last_msk(dut, db, username);
+		if (last_msk) {
+			if (strcmp(last_msk, "FAIL") == 0) {
+				snprintf(resp, sizeof(resp),
+					 "AuthStatus,FAIL,MSK,NULL");
+			} else {
+				snprintf(resp, sizeof(resp),
+					 "AuthStatus,SUCCESS,MSK,%s", last_msk);
+			}
+			free(last_msk);
+			break;
+		}
+		sleep(1);
+	}
+
+	sqlite3_close(db);
+
+	send_resp(dut, conn, SIGMA_COMPLETE, resp);
+	return 0;
+}
+
+
+static int get_last_serial_cb(void *ctx, int argc, char *argv[], char *col[])
+{
+	char **last_serial = ctx;
+
+	if (argc < 1 || !argv[0])
+		return 0;
+
+	free(*last_serial);
+	*last_serial = strdup(argv[0]);
+
+	return 0;
+}
+
+
+static char * get_last_serial(struct sigma_dut *dut, sqlite3 *db,
+			      const char *addr)
+{
+	char *sql, *last_serial = NULL;
+
+	sql = sqlite3_mprintf("SELECT serialnum FROM cert_enroll WHERE mac_addr=%Q",
+			      addr);
+	if (!sql)
+		return NULL;
+	sigma_dut_print(dut, DUT_MSG_DEBUG, "SQL: %s", sql);
+
+	if (sqlite3_exec(db, sql, get_last_serial_cb, &last_serial, NULL) !=
+	    SQLITE_OK) {
+		sigma_dut_print(dut, DUT_MSG_ERROR,
+				"SQL operation to fetch last_serial failed: %s",
+				sqlite3_errmsg(db));
+		sqlite3_free(sql);
+		return NULL;
+	}
+
+	sqlite3_free(sql);
+
+	return last_serial;
+}
+
+
+static int osu_cert_enroll_status(struct sigma_dut *dut,
+				  struct sigma_conn *conn,
+				  struct sigma_cmd *cmd, const char *addr,
+				  int timeout)
+{
+	sqlite3 *db;
+	int i;
+	char resp[500];
+
+	if (sqlite3_open(SERVER_DB, &db)) {
+		sigma_dut_print(dut, DUT_MSG_ERROR,
+				"Failed to open SQLite database %s",
+				SERVER_DB);
+		return -1;
+	}
+
+	snprintf(resp, sizeof(resp), "OSUStatus,TIMEOUT");
+
+	for (i = 0; i < timeout; i++) {
+		char *last_serial;
+
+		last_serial = get_last_serial(dut, db, addr);
+		if (last_serial) {
+			if (strcmp(last_serial, "FAIL") == 0) {
+				snprintf(resp, sizeof(resp),
+					 "OSUStatus,FAIL");
+			} else if (strlen(last_serial) > 0) {
+				snprintf(resp, sizeof(resp),
+					 "OSUStatus,SUCCESS,SerialNo,%s",
+					 last_serial);
+			}
+			free(last_serial);
+			break;
+		}
+		sleep(1);
+	}
+
+	sqlite3_close(db);
+
+	send_resp(dut, conn, SIGMA_COMPLETE, resp);
+	return 0;
+}
+
+
 static int cmd_server_request_status(struct sigma_dut *dut,
 				     struct sigma_conn *conn,
 				     struct sigma_cmd *cmd)
@@ -45,9 +412,17 @@
 	const char *var, *username, *serialno, *imsi, *addr, *status;
 	int osu, timeout;
 	char resp[500];
+	enum sigma_program prog;
 
 	var = get_param(cmd, "Program");
-	if (var == NULL || strcasecmp(var, "HS2-R2") != 0) {
+	if (!var) {
+		send_resp(dut, conn, SIGMA_ERROR,
+			  "errorCode,Missing program parameter");
+		return 0;
+	}
+
+	prog = sigma_program_to_enum(var);
+	if (prog != PROGRAM_HS2_R2 && prog != PROGRAM_HS2_R3) {
 		send_resp(dut, conn, SIGMA_ERROR,
 			  "errorCode,Unsupported program");
 		return 0;
@@ -97,14 +472,225 @@
 		return 0;
 	}
 
+	if (!osu && status && strcasecmp(status, "Authentication") == 0 &&
+	    username)
+		return aaa_auth_status(dut, conn, cmd, username, timeout);
+
+	if (osu && status && strcasecmp(status, "OSU") == 0 && addr)
+		return osu_cert_enroll_status(dut, conn, cmd, addr, timeout);
+
+	return 1;
+}
+
+
+static int cmd_server_set_parameter(struct sigma_dut *dut,
+				    struct sigma_conn *conn,
+				    struct sigma_cmd *cmd)
+{
+	const char *var, *root_ca, *inter_ca, *osu_cert, *issuing_arch, *name;
+	int osu, timeout = -1;
+	enum sigma_program prog;
+
+	var = get_param(cmd, "Program");
+	if (!var) {
+		send_resp(dut, conn, SIGMA_ERROR,
+			  "errorCode,Missing program parameter");
+		return 0;
+	}
+
+	prog = sigma_program_to_enum(var);
+	if (prog != PROGRAM_HS2_R2 && prog != PROGRAM_HS2_R3) {
+		send_resp(dut, conn, SIGMA_ERROR,
+			  "errorCode,Unsupported program");
+		return 0;
+	}
+
+	var = get_param(cmd, "Device");
+	if (!var ||
+	    (strcasecmp(var, "AAAServer") != 0 &&
+	     strcasecmp(var, "OSUServer") != 0)) {
+		send_resp(dut, conn, SIGMA_ERROR,
+			  "errorCode,Unsupported device type");
+		return 0;
+	}
+	osu = strcasecmp(var, "OSUServer") == 0;
+
+	var = get_param(cmd, "Timeout");
+	if (var)
+		timeout = atoi(var);
+
+	var = get_param(cmd, "ProvisioningProto");
+	if (var && strcasecmp(var, "SOAP") != 0) {
+		send_resp(dut, conn, SIGMA_ERROR,
+			  "errorCode,Unsupported ProvisioningProto");
+		return 0;
+	}
+
+	name = get_param(cmd, "Name");
+	root_ca = get_param(cmd, "TrustRootCACert");
+	inter_ca = get_param(cmd, "InterCACert");
+	osu_cert = get_param(cmd, "OSUServerCert");
+	issuing_arch = get_param(cmd, "Issuing_Arch");
+
+	/* TODO: CertReEnroll,{Enable|Disable} */
+	/* TODO: SerialNo,<hex> */
+
+	if (timeout > -1) {
+		/* TODO */
+	}
+
+	if (osu && name && root_ca && inter_ca && osu_cert && issuing_arch) {
+		const char *srv;
+		char buf[500];
+		char buf2[500];
+		int col;
+
+		sigma_dut_print(dut, DUT_MSG_DEBUG,
+				"Update server certificate setup");
+
+		if (strcasecmp(name, "ruckus") == 0) {
+			srv = "RKS";
+		} else if (strcasecmp(name, "aruba") == 0) {
+			srv = "ARU";
+		} else {
+			send_resp(dut, conn, SIGMA_ERROR,
+				  "errorCode,Unsupported Name value");
+			return 0;
+		}
+
+		if (strcasecmp(issuing_arch, "col2") == 0) {
+			col = 2;
+		} else if (strcasecmp(issuing_arch, "col4") == 0) {
+			col = 4;
+		} else {
+			send_resp(dut, conn, SIGMA_ERROR,
+				  "errorCode,Unsupported Issuing_Arch value");
+			return 0;
+		}
+
+		if (strcasecmp(root_ca, "ID-T") == 0) {
+			sigma_dut_print(dut, DUT_MSG_DEBUG,
+					"OSU trust root: NetworkFX");
+			if (system("cp " CERT_DIR "/IDT-cert-RootCA.pem "
+				   CERT_DIR "/cacert.pem") < 0)
+				return -2;
+		} else if (strcasecmp(root_ca, "ID-Y") == 0) {
+			sigma_dut_print(dut, DUT_MSG_DEBUG,
+					"OSU trust root: NetworkFX");
+			if (system("cp " CERT_DIR "/IDY-cert-RootCA.pem "
+				   CERT_DIR "/cacert.pem") < 0)
+				return -2;
+		} else {
+			send_resp(dut, conn, SIGMA_ERROR,
+				  "errorCode,Unsupported TrustRootCACert value");
+			return 0;
+		}
+
+		if (strcasecmp(inter_ca, "ID-Z.2") == 0) {
+			sigma_dut_print(dut, DUT_MSG_DEBUG,
+					"OSU intermediate CA: NetworkFX (col2)");
+			if (system("cat " CERT_DIR "/IDZ2-cert-InterCA.pem >> "
+				   CERT_DIR "/cacert.pem") < 0)
+				return -2;
+		} else if (strcasecmp(inter_ca, "ID-Z.4") == 0) {
+			sigma_dut_print(dut, DUT_MSG_DEBUG,
+					"OSU intermediate CA: DigiCert (col2)");
+			if (system("cat " CERT_DIR "/IDZ4-cert-InterCA.pem >> "
+				   CERT_DIR "/cacert.pem") < 0)
+				return -2;
+		} else if (strcasecmp(inter_ca, "ID-Z.6") == 0) {
+			sigma_dut_print(dut, DUT_MSG_DEBUG,
+					"OSU intermediate CA: NetworkFX (col4)");
+			if (system("cat " CERT_DIR "/IDZ6-cert-InterCA.pem >> "
+				   CERT_DIR "/cacert.pem") < 0)
+				return -2;
+		} else if (strcasecmp(inter_ca, "ID-Z.8") == 0) {
+			sigma_dut_print(dut, DUT_MSG_DEBUG,
+					"OSU intermediate CA: DigiCert (col4)");
+			if (system("cat " CERT_DIR "/IDZ8-cert-InterCA.pem >> "
+				   CERT_DIR "/cacert.pem") < 0)
+				return -2;
+		} else {
+			send_resp(dut, conn, SIGMA_ERROR,
+				  "errorCode,Unsupported InterCACert value");
+			return 0;
+		}
+
+		if (strcasecmp(osu_cert, "ID-Q") == 0) {
+			sigma_dut_print(dut, DUT_MSG_DEBUG,
+					"OSU server cert: NetworkFX col%d",
+					col);
+			snprintf(buf, sizeof(buf),
+				 "cp " CERT_DIR "/IDQ-cert-c%d-%s.pem "
+				 CERT_DIR "/server.pem",
+				 col, srv);
+			snprintf(buf2, sizeof(buf2),
+				 "cp " CERT_DIR "/IDQ-key-%s.pem "
+				 CERT_DIR "/server.key", srv);
+		} else if (strcasecmp(osu_cert, "ID-W") == 0) {
+			sigma_dut_print(dut, DUT_MSG_DEBUG,
+					"OSU server cert: DigiCert col%d",
+					col);
+			snprintf(buf, sizeof(buf),
+				 "cp " CERT_DIR "/IDW-cert-c%d-%s.pem "
+				 CERT_DIR "/server.pem",
+				 col, srv);
+			snprintf(buf2, sizeof(buf2),
+				 "cp " CERT_DIR "/IDW-key-%s.pem "
+				 CERT_DIR "/server.key", srv);
+		} else if (strcasecmp(osu_cert, "ID-R.2") == 0) {
+			sigma_dut_print(dut, DUT_MSG_DEBUG,
+					"OSU server cert: NetworkFX revoked col%d",
+					col);
+			snprintf(buf, sizeof(buf),
+				 "cp " CERT_DIR "/IDR2-cert-c%d-%s.pem "
+				 CERT_DIR "/server.pem",
+				 col, srv);
+			snprintf(buf2, sizeof(buf2),
+				 "cp " CERT_DIR "/IDR2-key-%s.pem "
+				 CERT_DIR "/server.key", srv);
+		} else if (strcasecmp(osu_cert, "ID-R.4") == 0) {
+			sigma_dut_print(dut, DUT_MSG_DEBUG,
+					"OSU server cert: DigiCert revoked col%d",
+					col);
+			snprintf(buf, sizeof(buf),
+				 "cp " CERT_DIR "/IDR4-cert-c%d-%s.pem "
+				 CERT_DIR "/server.pem",
+				 col, srv);
+			snprintf(buf2, sizeof(buf2),
+				 "cp " CERT_DIR "/IDR4-key-%s.pem "
+				 CERT_DIR "/server.key", srv);
+		} else {
+			send_resp(dut, conn, SIGMA_ERROR,
+				  "errorCode,Unsupported OSUServerCert value");
+			return 0;
+		}
+
+		if (system(buf) < 0 || system(buf2) < 0)
+			return -2;
+
+		if (system("service apache2 reload") < 0) {
+			send_resp(dut, conn, SIGMA_ERROR,
+				  "errorCode,Failed to restart Apache");
+			return 0;
+		}
+	}
+
+	/* TODO */
 	return 1;
 }
 
 
 void server_register_cmds(void)
 {
+	sigma_dut_reg_cmd("server_ca_get_version", NULL,
+			  cmd_server_ca_get_version);
+	sigma_dut_reg_cmd("server_get_info", NULL,
+			  cmd_server_get_info);
 	sigma_dut_reg_cmd("server_reset_default", NULL,
 			  cmd_server_reset_default);
 	sigma_dut_reg_cmd("server_request_status", NULL,
 			  cmd_server_request_status);
+	sigma_dut_reg_cmd("server_set_parameter", NULL,
+			  cmd_server_set_parameter);
 }
diff --git a/sigma_dut.c b/sigma_dut.c
index 3f71819..64aa41f 100644
--- a/sigma_dut.c
+++ b/sigma_dut.c
@@ -1094,7 +1094,6 @@
 	free(sigma_dut.rsne_override);
 	free(sigma_dut.ap_sae_groups);
 	free(sigma_dut.dpp_peer_uri);
-	free(sigma_dut.ap_tnc_url);
 #ifdef NL80211_SUPPORT
 	nl80211_deinit(&sigma_dut, sigma_dut.nl_ctx);
 #endif /* NL80211_SUPPORT */
diff --git a/sigma_dut.h b/sigma_dut.h
index f2fe3c3..7cd2715 100644
--- a/sigma_dut.h
+++ b/sigma_dut.h
@@ -67,7 +67,7 @@
 struct sigma_dut;
 
 #define MAX_PARAMS 100
-#define MAX_RADIO 2
+#define MAX_RADIO 3
 
 /* Set default operating channel width 80 MHz */
 #define VHT_DEFAULT_OPER_CHWIDTH AP_80_VHT_OPER_CHWIDTH
@@ -92,7 +92,7 @@
 	int count;
 };
 
-#define MAX_CMD_LEN 2048
+#define MAX_CMD_LEN 4096
 
 struct sigma_conn {
 	int s;
@@ -403,6 +403,12 @@
 		AP_SUITEB,
 		AP_WPA2_OWE,
 		AP_WPA2_EAP_OSEN,
+		AP_WPA2_FT_EAP,
+		AP_WPA2_FT_PSK,
+		AP_WPA2_EAP_SHA256,
+		AP_WPA2_PSK_SHA256,
+		AP_WPA2_ENT_FT_EAP,
+		AP_OSEN,
 	} ap_key_mgmt;
 	enum ap_tag_key_mgmt {
 		AP2_OPEN,
@@ -491,7 +497,6 @@
 	int ap_oper_icon_metadata;
 	int ap_tnc_file_name;
 	unsigned int ap_tnc_time_stamp;
-	char *ap_tnc_url;
 
 	int ap_fake_pkhash;
 	int ap_disable_protection;
@@ -667,6 +672,7 @@
 		PROGRAM_OCE,
 		PROGRAM_WPA3,
 		PROGRAM_HE,
+		PROGRAM_HS2_R3,
 	} program;
 
 	enum device_type {
@@ -739,6 +745,10 @@
 #endif /* NL80211_SUPPORT */
 
 	int sta_nss;
+
+#ifdef ANDROID
+	int nanservicediscoveryinprogress;
+#endif /* ANDROID */
 };
 
 
@@ -866,6 +876,7 @@
 		      unsigned char *addr);
 unsigned int channel_to_freq(unsigned int channel);
 unsigned int freq_to_channel(unsigned int freq);
+int is_ipv6_addr(const char *str);
 void convert_mac_addr_to_ipv6_lladdr(u8 *mac_addr, char *ipv6_buf,
 				     size_t buf_len);
 
diff --git a/sta.c b/sta.c
index eb3d744..352ea43 100644
--- a/sta.c
+++ b/sta.c
@@ -453,14 +453,6 @@
 }
 
 
-int is_ipv6_addr(const char *str)
-{
-	struct sockaddr_in6 addr;
-
-	return inet_pton(AF_INET6, str, &(addr.sin6_addr));
-}
-
-
 int get_ip_config(struct sigma_dut *dut, const char *ifname, char *buf,
 		  size_t buf_len)
 {
@@ -5804,9 +5796,9 @@
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
 			QCA_NL80211_VENDOR_SUBCMD_WIFI_TEST_CONFIGURATION) ||
 	    !(params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
-	    nla_put_u8(msg,
-		       QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ADDBA_BUFF_SIZE,
-		       bufsize)) {
+	    nla_put_u16(msg,
+			QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ADDBA_BUFF_SIZE,
+			bufsize)) {
 		sigma_dut_print(dut, DUT_MSG_ERROR,
 				"%s: err in adding vendor_cmd and vendor_data",
 				__func__);
@@ -5880,6 +5872,152 @@
 }
 
 
+static int sta_set_beamformee_sts(struct sigma_dut *dut, const char *intf,
+				  int val)
+{
+#ifdef NL80211_SUPPORT
+	struct nl_msg *msg;
+	int ret = 0;
+	struct nlattr *params;
+	int ifindex;
+
+	ifindex = if_nametoindex(intf);
+	if (ifindex == 0) {
+		sigma_dut_print(dut, DUT_MSG_ERROR,
+				"%s: Index for interface %s failed, val:%d",
+				__func__, intf, val);
+		return -1;
+	}
+
+	if (!(msg = nl80211_drv_msg(dut, dut->nl_ctx, ifindex, 0,
+				    NL80211_CMD_VENDOR)) ||
+	    nla_put_u32(msg, NL80211_ATTR_IFINDEX, ifindex) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+			QCA_NL80211_VENDOR_SUBCMD_WIFI_TEST_CONFIGURATION) ||
+	    !(params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
+	    nla_put_u8(msg,
+		       QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_TX_BEAMFORMEE_NSTS,
+		       val)) {
+		sigma_dut_print(dut, DUT_MSG_ERROR,
+				"%s: err in adding vendor_cmd and vendor_data, val: %d",
+				__func__, val);
+		nlmsg_free(msg);
+		return -1;
+	}
+	nla_nest_end(msg, params);
+
+	ret = send_and_recv_msgs(dut, dut->nl_ctx, msg, NULL, NULL);
+	if (ret) {
+		sigma_dut_print(dut, DUT_MSG_ERROR,
+				"%s: err in send_and_recv_msgs, ret=%d, val=%d",
+				__func__, ret, val);
+	}
+	return ret;
+#else /* NL80211_SUPPORT */
+	sigma_dut_print(dut, DUT_MSG_ERROR,
+			"beamformee sts cannot be changed without NL80211_SUPPORT defined");
+	return -1;
+#endif /* NL80211_SUPPORT */
+}
+
+
+#ifdef NL80211_SUPPORT
+static int sta_set_mac_padding_duration(struct sigma_dut *dut, const char *intf,
+					enum qca_wlan_he_mac_padding_dur val)
+{
+	struct nl_msg *msg;
+	int ret = 0;
+	struct nlattr *params;
+	int ifindex;
+
+	ifindex = if_nametoindex(intf);
+	if (ifindex == 0) {
+		sigma_dut_print(dut, DUT_MSG_ERROR,
+				"%s: Index for interface %s failed, val:%d",
+				__func__, intf, val);
+		return -1;
+	}
+
+	if (!(msg = nl80211_drv_msg(dut, dut->nl_ctx, ifindex, 0,
+				    NL80211_CMD_VENDOR)) ||
+	    nla_put_u32(msg, NL80211_ATTR_IFINDEX, ifindex) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+			QCA_NL80211_VENDOR_SUBCMD_WIFI_TEST_CONFIGURATION) ||
+	    !(params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
+	    nla_put_u8(msg,
+		       QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_MAC_PADDING_DUR,
+		       val)) {
+		sigma_dut_print(dut, DUT_MSG_ERROR,
+				"%s: err in adding vendor_cmd and vendor_data, val: %d",
+				__func__, val);
+		nlmsg_free(msg);
+		return -1;
+	}
+	nla_nest_end(msg, params);
+
+	ret = send_and_recv_msgs(dut, dut->nl_ctx, msg, NULL, NULL);
+	if (ret) {
+		sigma_dut_print(dut, DUT_MSG_ERROR,
+				"%s: err in send_and_recv_msgs, ret=%d, val=%d",
+				__func__, ret, val);
+	}
+	return ret;
+}
+#endif /* NL80211_SUPPORT */
+
+
+static int sta_set_mu_edca_override(struct sigma_dut *dut, const char *intf,
+				    int val)
+{
+#ifdef NL80211_SUPPORT
+	struct nl_msg *msg;
+	int ret = 0;
+	struct nlattr *params;
+	int ifindex;
+
+	ifindex = if_nametoindex(intf);
+	if (ifindex == 0) {
+		sigma_dut_print(dut, DUT_MSG_ERROR,
+				"%s: Index for interface %s failed, val:%d",
+				__func__, intf, val);
+		return -1;
+	}
+
+	if (!(msg = nl80211_drv_msg(dut, dut->nl_ctx, ifindex, 0,
+				    NL80211_CMD_VENDOR)) ||
+	    nla_put_u32(msg, NL80211_ATTR_IFINDEX, ifindex) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+			QCA_NL80211_VENDOR_SUBCMD_WIFI_TEST_CONFIGURATION) ||
+	    !(params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
+	    nla_put_u8(msg,
+		       QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_OVERRIDE_MU_EDCA,
+		       val)) {
+		sigma_dut_print(dut, DUT_MSG_ERROR,
+				"%s: err in adding vendor_cmd and vendor_data, val: %d",
+				__func__, val);
+		nlmsg_free(msg);
+		return -1;
+	}
+	nla_nest_end(msg, params);
+
+	ret = send_and_recv_msgs(dut, dut->nl_ctx, msg, NULL, NULL);
+	if (ret) {
+		sigma_dut_print(dut, DUT_MSG_ERROR,
+				"%s: err in send_and_recv_msgs, ret=%d, val=%d",
+				__func__, ret, val);
+	}
+	return ret;
+#else /* NL80211_SUPPORT */
+	sigma_dut_print(dut, DUT_MSG_ERROR,
+			"MU EDCA override cannot be changed without NL80211_SUPPORT defined");
+	return -1;
+#endif /* NL80211_SUPPORT */
+}
+
+
 static void sta_reset_default_wcn(struct sigma_dut *dut, const char *intf,
 				  const char *type)
 {
@@ -5949,6 +6087,23 @@
 			sigma_dut_print(dut, DUT_MSG_ERROR,
 					"Set LTF config to default in sta_reset_default_wcn failed");
 		}
+
+		if (sta_set_beamformee_sts(dut, intf, 0)) {
+			sigma_dut_print(dut, DUT_MSG_ERROR,
+					"Failed to set BeamformeeSTS");
+		}
+
+		if (sta_set_mac_padding_duration(
+			    dut, intf,
+			    QCA_WLAN_HE_NO_ADDITIONAL_PROCESS_TIME)) {
+			sigma_dut_print(dut, DUT_MSG_ERROR,
+					"Failed to set MAC padding duration");
+		}
+
+		if (sta_set_mu_edca_override(dut, intf, 0)) {
+			sigma_dut_print(dut, DUT_MSG_ERROR,
+					"ErrorCode,Failed to set MU EDCA override disable");
+		}
 #endif /* NL80211_SUPPORT */
 
 		if (sta_set_tx_beamformee(dut, intf, 1)) {
@@ -6091,7 +6246,7 @@
 		nan_cmd_sta_reset_default(dut, conn, cmd);
 #endif /* ANDROID_NAN */
 
-	if (dut->program == PROGRAM_HS2_R2) {
+	if (dut->program == PROGRAM_HS2_R2 || dut->program == PROGRAM_HS2_R3) {
 		unlink("SP/wi-fi.org/pps.xml");
 		if (system("rm -r SP/*") != 0) {
 		}
@@ -6142,12 +6297,14 @@
 
 	set_ps(intf, dut, 0);
 
-	if (dut->program == PROGRAM_HS2 || dut->program == PROGRAM_HS2_R2) {
+	if (dut->program == PROGRAM_HS2 || dut->program == PROGRAM_HS2_R2 ||
+	    dut->program == PROGRAM_HS2_R3) {
 		wpa_command(intf, "SET interworking 1");
 		wpa_command(intf, "SET hs20 1");
 	}
 
 	if (dut->program == PROGRAM_HS2_R2 ||
+	    dut->program == PROGRAM_HS2_R3 ||
 	    dut->program == PROGRAM_OCE) {
 		wpa_command(intf, "SET pmf 1");
 	} else {
@@ -6218,6 +6375,8 @@
 		return sta_cfon_reset_default(dut, conn, cmd);
 	}
 
+	wpa_command(intf, "SET setband AUTO");
+
 	if (dut->program != PROGRAM_VHT)
 		return cmd_sta_p2p_reset(dut, conn, cmd);
 
@@ -6424,10 +6583,12 @@
 {
 	const char *intf = get_param(cmd, "Interface");
 	const char *val;
-	char buf[30];
+	const char *program;
+	char buf[60];
 	int tkip = -1;
 	int wep = -1;
 
+	program = get_param(cmd, "Program");
 	val = get_param(cmd, "SGI80");
 	if (val) {
 		int sgi80;
@@ -6442,15 +6603,35 @@
 
 	val = get_param(cmd, "TxBF");
 	if (val && (strcmp(val, "1") == 0 || strcasecmp(val, "Enable") == 0)) {
-		snprintf(buf, sizeof(buf), "iwpriv %s vhtsubfee 1", intf);
-		if (system(buf) != 0) {
+		switch (get_driver_type()) {
+		case DRIVER_WCN:
+			if (sta_set_tx_beamformee(dut, intf, 1)) {
+				send_resp(dut, conn, SIGMA_ERROR,
+					  "ErrorCode,Failed to set TX beamformee enable");
+				return 0;
+			}
+			break;
+		case DRIVER_ATHEROS:
+			snprintf(buf, sizeof(buf), "iwpriv %s vhtsubfee 1",
+				 intf);
+			if (system(buf) != 0) {
+				send_resp(dut, conn, SIGMA_ERROR,
+					  "ErrorCode,Setting vhtsubfee failed");
+				return 0;
+			}
+
+			snprintf(buf, sizeof(buf), "iwpriv %s vhtsubfer 1",
+				 intf);
+			if (system(buf) != 0) {
+				send_resp(dut, conn, SIGMA_ERROR,
+					  "ErrorCode,Setting vhtsubfer failed");
+				return 0;
+			}
+			break;
+		default:
 			sigma_dut_print(dut, DUT_MSG_ERROR,
-					"iwpriv vhtsubfee failed");
-		}
-		snprintf(buf, sizeof(buf), "iwpriv %s vhtsubfer 1", intf);
-		if (system(buf) != 0) {
-			sigma_dut_print(dut, DUT_MSG_ERROR,
-					"iwpriv vhtsubfer failed");
+					"Unsupported driver type");
+			break;
 		}
 	}
 
@@ -6460,27 +6641,31 @@
 		case DRIVER_ATHEROS:
 			ath_sta_set_txsp_stream(dut, intf, "1SS");
 			ath_sta_set_rxsp_stream(dut, intf, "1SS");
+			snprintf(buf, sizeof(buf), "iwpriv %s vhtmubfee 1",
+				 intf);
+			if (system(buf) != 0) {
+				sigma_dut_print(dut, DUT_MSG_ERROR,
+						"iwpriv vhtmubfee failed");
+			}
+			snprintf(buf, sizeof(buf), "iwpriv %s vhtmubfer 1",
+				 intf);
+			if (system(buf) != 0) {
+				sigma_dut_print(dut, DUT_MSG_ERROR,
+						"iwpriv vhtmubfer failed");
+			}
+			break;
 		case DRIVER_WCN:
 			if (wcn_sta_set_sp_stream(dut, intf, "1SS") < 0) {
 				send_resp(dut, conn, SIGMA_ERROR,
 					  "ErrorCode,Failed to set RX/TXSP_STREAM");
 				return 0;
 			}
+			break;
 		default:
 			sigma_dut_print(dut, DUT_MSG_ERROR,
 					"Setting SP_STREAM not supported");
 			break;
 		}
-		snprintf(buf, sizeof(buf), "iwpriv %s vhtmubfee 1", intf);
-		if (system(buf) != 0) {
-			sigma_dut_print(dut, DUT_MSG_ERROR,
-					"iwpriv vhtmubfee failed");
-		}
-		snprintf(buf, sizeof(buf), "iwpriv %s vhtmubfer 1", intf);
-		if (system(buf) != 0) {
-			sigma_dut_print(dut, DUT_MSG_ERROR,
-					"iwpriv vhtmubfer failed");
-		}
 	}
 
 	val = get_param(cmd, "LDPC");
@@ -6659,7 +6844,9 @@
 		result = strtok_r(token, ";", &saveptr);
 		if (!result) {
 			sigma_dut_print(dut, DUT_MSG_ERROR,
-					"VHT NSS not specified");
+					"NSS not specified");
+			send_resp(dut, conn, SIGMA_ERROR,
+				  "errorCode,NSS not specified");
 			return 0;
 		}
 		nss = atoi(result);
@@ -6674,82 +6861,123 @@
 		result = strtok_r(NULL, ";", &saveptr);
 		if (result == NULL) {
 			sigma_dut_print(dut, DUT_MSG_ERROR,
-					"VHTMCS NOT SPECIFIED!");
+					"MCS not specified");
+			send_resp(dut, conn, SIGMA_ERROR,
+				  "errorCode,MCS not specified");
 			return 0;
 		}
 		result = strtok_r(result, "-", &saveptr);
 		result = strtok_r(NULL, "-", &saveptr);
 		if (!result) {
 			sigma_dut_print(dut, DUT_MSG_ERROR,
-					"VHT MCS not specified");
+					"MCS not specified");
+			send_resp(dut, conn, SIGMA_ERROR,
+				  "errorCode,MCS not specified");
 			return 0;
 		}
 		mcs = atoi(result);
 
-		snprintf(buf, sizeof(buf), "iwpriv %s vhtmcs %d", intf, mcs);
-		if (system(buf) != 0) {
-			sigma_dut_print(dut, DUT_MSG_ERROR,
-					"iwpriv mcs failed");
-		}
+		if (program && strcasecmp(program, "HE") == 0) {
+#ifdef NL80211_SUPPORT
+			enum he_mcs_config mcs_config;
+			int ret;
 
-		switch (nss) {
-		case 1:
-			switch (mcs) {
-			case 7:
-				vht_mcsmap = 0xfffc;
-				break;
-			case 8:
-				vht_mcsmap = 0xfffd;
-				break;
-			case 9:
-				vht_mcsmap = 0xfffe;
-				break;
-			default:
-				vht_mcsmap = 0xfffe;
-				break;
+			if (mcs >= 0 && mcs <= 7) {
+				mcs_config = HE_80_MCS0_7;
+			} else if (mcs > 7 && mcs <= 9) {
+				mcs_config = HE_80_MCS0_9;
+			} else if (mcs > 9 && mcs <= 11) {
+				mcs_config = HE_80_MCS0_11;
+			} else {
+				sigma_dut_print(dut, DUT_MSG_ERROR,
+						"nss_mcs_cap: HE: Invalid mcs: %d",
+						mcs);
+				send_resp(dut, conn, SIGMA_ERROR,
+					  "errorCode,Invalid MCS");
+				return 0;
 			}
-			break;
-		case 2:
-			switch (mcs) {
-			case 7:
-				vht_mcsmap = 0xfff0;
-				break;
-			case 8:
-				vht_mcsmap = 0xfff5;
-				break;
-			case 9:
-				vht_mcsmap = 0xfffa;
-				break;
-			default:
-				vht_mcsmap = 0xfffa;
-				break;
+
+			ret = sta_set_he_mcs(dut, intf, mcs_config);
+			if (ret) {
+				sigma_dut_print(dut, DUT_MSG_ERROR,
+						"nss_mcs_cap: HE: Setting of MCS failed, mcs_config: %d, ret: %d",
+						mcs_config, ret);
+				send_resp(dut, conn, SIGMA_ERROR,
+					  "errorCode,Failed to set MCS");
+				return 0;
 			}
-			break;
-		case 3:
-			switch (mcs) {
-			case 7:
-				vht_mcsmap = 0xffc0;
-				break;
-			case 8:
-				vht_mcsmap = 0xffd5;
-				break;
-			case 9:
-				vht_mcsmap = 0xffea;
-				break;
-			default:
-				vht_mcsmap = 0xffea;
-				break;
-			}
-			break;
-		default:
-			vht_mcsmap = 0xffea;
-			break;
-		}
-		snprintf(buf, sizeof(buf), "iwpriv %s vht_mcsmap 0x%04x",
-			 intf, vht_mcsmap);
-		if (system(buf) != 0) {
+#else /* NL80211_SUPPORT */
 			sigma_dut_print(dut, DUT_MSG_ERROR,
-					"iwpriv vht_mcsmap failed");
+					"nss_mcs_cap: HE: MCS cannot be changed without NL80211_SUPPORT defined");
+#endif /* NL80211_SUPPORT */
+		} else {
+			snprintf(buf, sizeof(buf), "iwpriv %s vhtmcs %d",
+				 intf, mcs);
+			if (system(buf) != 0) {
+				sigma_dut_print(dut, DUT_MSG_ERROR,
+						"iwpriv mcs failed");
+			}
+
+			switch (nss) {
+			case 1:
+				switch (mcs) {
+				case 7:
+					vht_mcsmap = 0xfffc;
+					break;
+				case 8:
+					vht_mcsmap = 0xfffd;
+					break;
+				case 9:
+					vht_mcsmap = 0xfffe;
+					break;
+				default:
+					vht_mcsmap = 0xfffe;
+					break;
+				}
+				break;
+			case 2:
+				switch (mcs) {
+				case 7:
+					vht_mcsmap = 0xfff0;
+					break;
+				case 8:
+					vht_mcsmap = 0xfff5;
+					break;
+				case 9:
+					vht_mcsmap = 0xfffa;
+					break;
+				default:
+					vht_mcsmap = 0xfffa;
+					break;
+				}
+				break;
+			case 3:
+				switch (mcs) {
+				case 7:
+					vht_mcsmap = 0xffc0;
+					break;
+				case 8:
+					vht_mcsmap = 0xffd5;
+					break;
+				case 9:
+					vht_mcsmap = 0xffea;
+					break;
+				default:
+					vht_mcsmap = 0xffea;
+					break;
+				}
+				break;
+			default:
+				vht_mcsmap = 0xffea;
+				break;
+			}
+			snprintf(buf, sizeof(buf),
+				 "iwpriv %s vht_mcsmap 0x%04x",
+				 intf, vht_mcsmap);
+			if (system(buf) != 0) {
+				sigma_dut_print(dut, DUT_MSG_ERROR,
+						"iwpriv vht_mcsmap failed");
+			}
 		}
 	}
 
@@ -6782,29 +7010,112 @@
 		}
 	}
 
-	 val = get_param(cmd, "txBandwidth");
-	 if (val) {
-		 switch (get_driver_type()) {
-		 case DRIVER_WCN:
-			 if (wcn_sta_set_width(dut, intf, val) < 0) {
-				 send_resp(dut, conn, SIGMA_ERROR,
-					   "ErrorCode,Failed to set txBandwidth");
-				 return 0;
-			 }
-			 break;
-		 case DRIVER_ATHEROS:
-			 if (ath_set_width(dut, conn, intf, val) < 0) {
-				 send_resp(dut, conn, SIGMA_ERROR,
-					   "ErrorCode,Failed to set txBandwidth");
-				 return 0;
-			 }
-			 break;
-		 default:
-			 sigma_dut_print(dut, DUT_MSG_ERROR,
-					 "Setting txBandwidth not supported");
-			 break;
-		 }
-	 }
+	val = get_param(cmd, "txBandwidth");
+	if (val) {
+		switch (get_driver_type()) {
+		case DRIVER_WCN:
+			if (wcn_sta_set_width(dut, intf, val) < 0) {
+				send_resp(dut, conn, SIGMA_ERROR,
+					  "ErrorCode,Failed to set txBandwidth");
+				return 0;
+			}
+			break;
+		case DRIVER_ATHEROS:
+			if (ath_set_width(dut, conn, intf, val) < 0) {
+				send_resp(dut, conn, SIGMA_ERROR,
+					  "ErrorCode,Failed to set txBandwidth");
+				return 0;
+			}
+			break;
+		default:
+			sigma_dut_print(dut, DUT_MSG_ERROR,
+					"Setting txBandwidth not supported");
+			break;
+		}
+	}
+
+	val = get_param(cmd, "BeamformeeSTS");
+	if (val) {
+		if (sta_set_tx_beamformee(dut, intf, 1)) {
+			send_resp(dut, conn, SIGMA_ERROR,
+					"ErrorCode,Failed to set TX beamformee enable");
+			return 0;
+		}
+
+		if (sta_set_beamformee_sts(dut, intf, atoi(val))) {
+			send_resp(dut, conn, SIGMA_ERROR,
+				  "ErrorCode,Failed to set BeamformeeSTS");
+			return 0;
+		}
+	}
+
+	val = get_param(cmd, "Trig_MAC_Padding_Dur");
+	if (val) {
+#ifdef NL80211_SUPPORT
+		enum qca_wlan_he_mac_padding_dur set_val;
+
+		switch (atoi(val)) {
+		case 16:
+			set_val = QCA_WLAN_HE_16US_OF_PROCESS_TIME;
+			break;
+		case 8:
+			set_val = QCA_WLAN_HE_8US_OF_PROCESS_TIME;
+			break;
+		default:
+			set_val = QCA_WLAN_HE_NO_ADDITIONAL_PROCESS_TIME;
+			break;
+		}
+		if (sta_set_mac_padding_duration(dut, intf, set_val)) {
+			send_resp(dut, conn, SIGMA_ERROR,
+				  "ErrorCode,Failed to set MAC padding duration");
+			return 0;
+		}
+#else /* NL80211_SUPPORT */
+		sigma_dut_print(dut, DUT_MSG_ERROR,
+				"MAC padding duration cannot be changed without NL80211_SUPPORT defined");
+#endif /* NL80211_SUPPORT */
+	}
+
+	val = get_param(cmd, "MU_EDCA");
+	if (val && (strcasecmp(val, "Override") == 0)) {
+		if (sta_set_mu_edca_override(dut, intf, 1)) {
+			send_resp(dut, conn, SIGMA_ERROR,
+				  "ErrorCode,Failed to set MU EDCA override");
+			return 0;
+		}
+	}
+
+	val = get_param(cmd, "ADDBAResp_BufSize");
+	if (val) {
+		int buf_size;
+
+		if (strcasecmp(val, "gt64") == 0)
+			buf_size = 256;
+		else
+			buf_size = 64;
+		if (get_driver_type() == DRIVER_WCN &&
+		    sta_set_addba_buf_size(dut, intf, buf_size)) {
+			send_resp(dut, conn, SIGMA_ERROR,
+				  "ErrorCode,set addbaresp_buff_size failed");
+			return 0;
+		}
+	}
+
+	val = get_param(cmd, "ADDBAReq_BufSize");
+	if (val) {
+		int buf_size;
+
+		if (strcasecmp(val, "gt64") == 0)
+			buf_size = 256;
+		else
+			buf_size = 64;
+		if (get_driver_type() == DRIVER_WCN &&
+		    sta_set_addba_buf_size(dut, intf, buf_size)) {
+			send_resp(dut, conn, SIGMA_ERROR,
+				  "ErrorCode,set addbareq_buff_size failed");
+			return 0;
+		}
+	}
 
 	return cmd_sta_set_wireless_common(intf, dut, conn, cmd);
 }
@@ -7137,9 +7448,9 @@
 		       QCA_WLAN_ADD_BA) ||
 	    nla_put_u8(msg, QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_BA_TID,
 		       tid) ||
-	    nla_put_u8(msg,
-		       QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ADDBA_BUFF_SIZE,
-		       bufsize)) {
+	    nla_put_u16(msg,
+			QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ADDBA_BUFF_SIZE,
+			bufsize)) {
 		sigma_dut_print(dut, DUT_MSG_ERROR,
 				"%s: err in adding vendor_cmd and vendor_data",
 				__func__);
@@ -7749,7 +8060,7 @@
 {
 	char buf[4096];
 
-	snprintf(buf, sizeof(buf), "BSS %s", bssid);
+	snprintf(buf, sizeof(buf), "BSS MASK=1 %s", bssid);
 	if (wpa_command_resp(ifname, buf, buf, sizeof(buf)) < 0)
 		return 0;
 	if (strncmp(buf, "id=", 3) != 0)
@@ -9605,6 +9916,7 @@
 {
 	const char *intf = get_param(cmd, "Interface");
 	const char *val = get_param(cmd, "Ignore_blacklist");
+	const char *band = get_param(cmd, "Band");
 	struct wpa_ctrl *ctrl;
 	int res;
 	char bssid[20], ssid[40], resp[100], buf[100], blacklisted[100];
@@ -9619,6 +9931,18 @@
 
 	start_sta_mode(dut);
 
+	if (band) {
+		if (strcmp(band, "2.4") == 0) {
+			wpa_command(intf, "SET setband 2G");
+		} else if (strcmp(band, "5") == 0) {
+			wpa_command(intf, "SET setband 5G");
+		} else {
+			send_resp(dut, conn, SIGMA_ERROR,
+				  "errorCode,Unsupported band");
+			return 0;
+		}
+	}
+
 	blacklisted[0] = '\0';
 	if (val && atoi(val))
 		ignore_blacklist = 1;
@@ -9707,6 +10031,84 @@
 }
 
 
+static int cmd_sta_hs2_venue_info(struct sigma_dut *dut,
+				  struct sigma_conn *conn,
+				  struct sigma_cmd *cmd)
+{
+	const char *intf = get_param(cmd, "Interface");
+	const char *display = get_param(cmd, "Display");
+	struct wpa_ctrl *ctrl;
+	char buf[300], params[400], *pos;
+	char bssid[20];
+	int info_avail = 0;
+	unsigned int old_timeout;
+	int res;
+
+	if (get_wpa_status(intf, "bssid", bssid, sizeof(bssid)) < 0) {
+		send_resp(dut, conn, SIGMA_ERROR,
+			  "ErrorCode,Could not get current BSSID");
+		return 0;
+	}
+	ctrl = open_wpa_mon(intf);
+	if (!ctrl) {
+		sigma_dut_print(dut, DUT_MSG_ERROR,
+				"Failed to open wpa_supplicant monitor connection");
+		return -2;
+	}
+
+	snprintf(buf, sizeof(buf), "ANQP_GET %s 277", bssid);
+	wpa_command(intf, buf);
+
+	res = get_wpa_cli_event(dut, ctrl, "GAS-QUERY-DONE", buf, sizeof(buf));
+	if (res < 0) {
+		send_resp(dut, conn, SIGMA_ERROR,
+			  "ErrorCode,Could not complete GAS query");
+		goto fail;
+	}
+
+	old_timeout = dut->default_timeout;
+	dut->default_timeout = 2;
+	res = get_wpa_cli_event(dut, ctrl, "RX-VENUE-URL", buf, sizeof(buf));
+	dut->default_timeout = old_timeout;
+	if (res < 0)
+		goto done;
+	pos = strchr(buf, ' ');
+	if (!pos)
+		goto done;
+	pos++;
+	pos = strchr(pos, ' ');
+	if (!pos)
+		goto done;
+	pos++;
+	info_avail = 1;
+	snprintf(params, sizeof(params), "browser %s", pos);
+
+	if (display && strcasecmp(display, "Yes") == 0) {
+		pid_t pid;
+
+		pid = fork();
+		if (pid < 0) {
+			perror("fork");
+			return -1;
+		}
+
+		if (pid == 0) {
+			run_hs20_osu(dut, params);
+			exit(0);
+		}
+	}
+
+done:
+	snprintf(buf, sizeof(buf), "Info_available,%s",
+		 info_avail ? "Yes" : "No");
+	send_resp(dut, conn, SIGMA_COMPLETE, buf);
+fail:
+	wpa_ctrl_detach(ctrl);
+	wpa_ctrl_close(ctrl);
+	return 0;
+}
+
+
 static int sta_add_credential_uname_pwd(struct sigma_dut *dut,
 					struct sigma_conn *conn,
 					const char *ifname,
@@ -9905,7 +10307,7 @@
 		return 0;
 	}
 
-	if (dut->program == PROGRAM_HS2_R2) {
+	if (dut->program == PROGRAM_HS2_R2 || dut->program == PROGRAM_HS2_R3) {
 		/*
 		 * Set provisioning_sp for the test cases where SIM/USIM
 		 * provisioning is used.
@@ -10248,13 +10650,14 @@
 		       struct sigma_cmd *cmd)
 {
 	const char *intf = get_param(cmd, "Interface");
-	const char *name, *val;
+	const char *name, *osu_ssid, *val;
 	int prod_ess_assoc = 1;
-	char buf[200], bssid[100], ssid[100];
+	char buf[300], bssid[100], ssid[100];
 	int res;
 	struct wpa_ctrl *ctrl;
 
 	name = get_param(cmd, "osuFriendlyName");
+	osu_ssid = get_param(cmd, "osu_ssid");
 
 	val = get_param(cmd, "ProdESSAssoc");
 	if (val)
@@ -10267,10 +10670,12 @@
 	sigma_dut_print(dut, DUT_MSG_DEBUG, "Trigger OSU");
 	mkdir("Logs", S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
 	res = snprintf(buf, sizeof(buf),
-		       "%s %s%s%s signup osu-ca.pem",
+		       "%s %s%s%s %s%s%s signup osu-ca.pem",
 		       prod_ess_assoc ? "" : "-N",
 		       name ? "-O'" : "", name ? name : "",
-		       name ? "'" : "");
+		       name ? "'" : "",
+		       osu_ssid ? "-o'" : "", osu_ssid ? osu_ssid : "",
+		       osu_ssid ? "'" : "");
 
 	hs2_set_policy(dut);
 	if (run_hs20_osu(dut, buf) < 0) {
@@ -10638,6 +11043,8 @@
 	sigma_dut_reg_cmd("sta_get_key", req_intf, cmd_sta_get_key);
 	sigma_dut_reg_cmd("sta_hs2_associate", req_intf,
 			  cmd_sta_hs2_associate);
+	sigma_dut_reg_cmd("sta_hs2_venue_info", req_intf,
+			  cmd_sta_hs2_venue_info);
 	sigma_dut_reg_cmd("sta_add_credential", req_intf,
 			  cmd_sta_add_credential);
 	sigma_dut_reg_cmd("sta_scan", req_intf, cmd_sta_scan);
diff --git a/traffic.c b/traffic.c
index f75d515..eb58258 100644
--- a/traffic.c
+++ b/traffic.c
@@ -22,22 +22,6 @@
 #endif /* ANDROID */
 
 
-static int is_ipv6_addr(const char *str)
-{
-	const char *pos = str;
-
-	while (*pos) {
-		if (*pos != ':' && (*pos < '0' || *pos > '9') &&
-		    (*pos < 'a' || *pos > 'f') &&
-		    (*pos < 'A' || *pos > 'F'))
-			return 0;
-		pos++;
-	}
-
-	return 1;
-}
-
-
 static int cmd_traffic_send_ping(struct sigma_dut *dut,
 				 struct sigma_conn *conn,
 				 struct sigma_cmd *cmd)
diff --git a/utils.c b/utils.c
index e92e0e5..36bf36d 100644
--- a/utils.c
+++ b/utils.c
@@ -95,9 +95,10 @@
 	if (strcasecmp(prog, "HS2") == 0)
 		return PROGRAM_HS2;
 	if (strcasecmp(prog, "HS2_R2") == 0 ||
-	    strcasecmp(prog, "HS2-R2") == 0 ||
-	    strcasecmp(prog, "HS2-R3") == 0)
+	    strcasecmp(prog, "HS2-R2") == 0)
 		return PROGRAM_HS2_R2;
+	if (strcasecmp(prog, "HS2-R3") == 0)
+		return PROGRAM_HS2_R3;
 	if (strcasecmp(prog, "WFD") == 0)
 		return PROGRAM_WFD;
 	if (strcasecmp(prog, "DisplayR2") == 0)
@@ -239,6 +240,14 @@
 }
 
 
+int is_ipv6_addr(const char *str)
+{
+	struct sockaddr_in6 addr;
+
+	return inet_pton(AF_INET6, str, &(addr.sin6_addr));
+}
+
+
 void convert_mac_addr_to_ipv6_lladdr(u8 *mac_addr, char *ipv6_buf,
 				     size_t buf_len)
 {