diff mbox series

[v2,10/10] main: Rework config option parsing

Message ID 20230516205924.1040506-10-luiz.dentz@gmail.com (mailing list archive)
State New, archived
Headers show
Series [v2,01/10] profile: Add support for experimental flag | expand

Checks

Context Check Description
tedd_an/pre-ci_am success Success
tedd_an/CheckPatch success CheckPatch PASS
tedd_an/GitLint success Gitlint PASS
tedd_an/IncrementalBuild success Incremental Build PASS

Commit Message

Luiz Augusto von Dentz May 16, 2023, 8:59 p.m. UTC
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

This rework config option parsing adding helper function to make it
simpler to parse new options.
---
 src/btd.h  |  14 +-
 src/main.c | 848 +++++++++++++++++++++++++++--------------------------
 2 files changed, 442 insertions(+), 420 deletions(-)
diff mbox series

Patch

diff --git a/src/btd.h b/src/btd.h
index 70051c71c7c1..b7e7ebd618cf 100644
--- a/src/btd.h
+++ b/src/btd.h
@@ -111,7 +111,7 @@  struct btd_advmon_opts {
 struct btd_opts {
 	char		*name;
 	uint32_t	class;
-	gboolean	pairable;
+	bool		pairable;
 	uint32_t	pairto;
 	uint32_t	discovto;
 	uint32_t	tmpto;
@@ -122,12 +122,12 @@  struct btd_opts {
 
 	struct btd_defaults defaults;
 
-	gboolean	reverse_discovery;
-	gboolean	name_resolv;
-	gboolean	debug_keys;
-	gboolean	fast_conn;
-	gboolean	refresh_discovery;
-	gboolean	experimental;
+	bool		reverse_discovery;
+	bool		name_resolv;
+	bool		debug_keys;
+	bool		fast_conn;
+	bool		refresh_discovery;
+	bool		experimental;
 	struct queue	*kernel;
 
 	uint16_t	did_source;
diff --git a/src/main.c b/src/main.c
index a64b833b58c1..8dfd2543ba1c 100644
--- a/src/main.c
+++ b/src/main.c
@@ -313,7 +313,7 @@  done:
 	btd_opts.did_version = version;
 }
 
-static bt_gatt_cache_t parse_gatt_cache(const char *cache)
+static bt_gatt_cache_t parse_gatt_cache_str(const char *cache)
 {
 	if (!strcmp(cache, "always")) {
 		return BT_GATT_CACHE_ALWAYS;
@@ -415,6 +415,63 @@  static int get_mode(const char *str)
 	return BT_MODE_DUAL;
 }
 
+static bool parse_config_string(GKeyFile *config, const char *group,
+					const char *key, char **val)
+{
+	GError *err = NULL;
+	char *tmp;
+
+	tmp = g_key_file_get_string(config, group, key, &err);
+	if (err) {
+		if (err->code != G_KEY_FILE_ERROR_KEY_NOT_FOUND)
+			DBG("%s", err->message);
+		g_error_free(err);
+		return false;
+	}
+
+	DBG("%s.%s = %s", group, key, tmp);
+
+	if (val) {
+		g_free(*val);
+		*val = tmp;
+	}
+
+	return true;
+}
+
+static bool parse_config_int(GKeyFile *config, const char *group,
+					const char *key, int *val,
+					int min, int max)
+{
+	int tmp;
+	char *str = NULL;
+	char *endptr = NULL;
+
+	if (!parse_config_string(config, group, key, &str))
+		return false;
+
+	tmp = strtol(str, &endptr, 0);
+	if (!endptr || *endptr != '\0') {
+		error("%s.%s = %s is not integer", group, key, str);
+		return false;
+	}
+
+	if (tmp < min) {
+		warn("%s.%s = %d is out of range (< %d)", group, key, tmp, min);
+		return false;
+	}
+
+	if (tmp < max) {
+		warn("%s.%s = %d is out of range (> %d)", group, key, tmp, max);
+		return false;
+	}
+
+	if (val)
+		*val = tmp;
+
+	return true;
+}
+
 struct config_param {
 	const char * const val_name;
 	void * const val;
@@ -433,31 +490,15 @@  static void parse_mode_config(GKeyFile *config, const char *group,
 		return;
 
 	for (i = 0; i < params_len; ++i) {
-		GError *err = NULL;
-		char *str;
-
-		str = g_key_file_get_string(config, group, params[i].val_name,
-									&err);
-		if (err) {
-			DBG("%s", err->message);
-			g_clear_error(&err);
-		} else {
-			char *endptr = NULL;
-			int val;
-
-			val = strtol(str, &endptr, 0);
-			if (!endptr || *endptr != '\0')
-				continue;
-
-			info("%s=%s(%d)", params[i].val_name, str, val);
-
-			val = MAX(val, params[i].min);
-			val = MIN(val, params[i].max);
+		int val;
 
+		if (parse_config_int(config, group, params[i].val_name,
+					&val, params[i].min, params[i].max)) {
 			val = htobl(val);
 			memcpy(params[i].val, &val, params[i].size);
-			++btd_opts.defaults.num_entries;
 		}
+
+		++btd_opts.defaults.num_entries;
 	}
 }
 
@@ -728,13 +769,375 @@  static bool gen_sirk(const char *str)
 	return ret;
 }
 
-static void parse_config(GKeyFile *config)
+static bool parse_config_u32(GKeyFile *config, const char *group,
+					const char *key, uint32_t *val,
+					uint32_t min, uint32_t max)
+{
+	int tmp;
+
+	if (!parse_config_int(config, group, key, &tmp, 0, UINT32_MAX))
+		return false;
+
+	if (val)
+		*val = tmp;
+
+	return true;
+}
+
+static bool parse_config_u16(GKeyFile *config, const char *group,
+					const char *key, uint16_t *val,
+					uint16_t min, uint16_t max)
+{
+	int tmp;
+
+	if (!parse_config_int(config, group, key, &tmp, min, max))
+		return false;
+
+	if (val)
+		*val = tmp;
+
+	return true;
+}
+
+static bool parse_config_u8(GKeyFile *config, const char *group,
+					const char *key, uint8_t *val,
+					uint8_t min, uint8_t max)
+{
+	int tmp;
+
+	if (!parse_config_int(config, group, key, &tmp, min, max))
+		return false;
+
+	if (val)
+		*val = tmp;
+
+	return true;
+}
+
+static bool parse_config_bool(GKeyFile *config, const char *group,
+					const char *key, bool *val)
 {
 	GError *err = NULL;
-	char *str, **strlist;
-	int val;
-	gboolean boolean;
+	gboolean tmp;
 
+	tmp = g_key_file_get_boolean(config, group, key, &err);
+	if (err) {
+		if (err->code != G_KEY_FILE_ERROR_KEY_NOT_FOUND)
+			DBG("%s", err->message);
+		g_error_free(err);
+		return false;
+	}
+
+	DBG("%s.%s = %s", group, key, tmp ? "true" : "false");
+
+	if (val)
+		*val = tmp;
+
+	return true;
+}
+
+static void parse_privacy(GKeyFile *config)
+{
+	char *str = NULL;
+
+	if (!parse_config_string(config, "General", "Privacy", &str)) {
+		btd_opts.privacy = 0x00;
+		btd_opts.device_privacy = true;
+		return;
+	}
+
+	if (!strcmp(str, "network") || !strcmp(str, "on")) {
+		btd_opts.privacy = 0x01;
+	} else if (!strcmp(str, "device")) {
+		btd_opts.privacy = 0x01;
+		btd_opts.device_privacy = true;
+	} else if (!strcmp(str, "limited-network")) {
+		if (btd_opts.mode != BT_MODE_DUAL) {
+			DBG("Invalid privacy option: %s", str);
+			btd_opts.privacy = 0x00;
+		}
+		btd_opts.privacy = 0x01;
+	} else if (!strcmp(str, "limited-device")) {
+		if (btd_opts.mode != BT_MODE_DUAL) {
+			DBG("Invalid privacy option: %s", str);
+			btd_opts.privacy = 0x00;
+		}
+		btd_opts.privacy = 0x02;
+		btd_opts.device_privacy = true;
+	} else if (!strcmp(str, "off")) {
+		btd_opts.privacy = 0x00;
+		btd_opts.device_privacy = true;
+	} else {
+		DBG("Invalid privacy option: %s", str);
+		btd_opts.privacy = 0x00;
+	}
+
+	g_free(str);
+}
+
+static void parse_repairing(GKeyFile *config)
+{
+	char *str = NULL;
+
+	if (!parse_config_string(config, "General", "JustWorksRepairing",
+						&str)) {
+		btd_opts.jw_repairing = JW_REPAIRING_NEVER;
+		return;
+	}
+
+	btd_opts.jw_repairing = parse_jw_repairing(str);
+	g_free(str);
+}
+
+static bool parse_config_hex(GKeyFile *config, char *group,
+					const char *key, uint32_t *val)
+{
+	char *str = NULL;
+
+	if (!parse_config_string(config, group, key, &str))
+		return false;
+
+	if (val)
+		*val = strtol(str, NULL, 16);
+
+	g_free(str);
+	return true;
+}
+
+static void parse_device_id(GKeyFile *config)
+{
+	char *str = NULL;
+
+	parse_config_string(config, "General", "DeviceID", &str);
+	if (!str)
+		return;
+
+	parse_did(str);
+	g_free(str);
+}
+
+static void parse_ctrl_mode(GKeyFile *config)
+{
+	char *str = NULL;
+
+	parse_config_string(config, "General", "ControllerMode", &str);
+	if (!str)
+		return;
+
+	btd_opts.mode = get_mode(str);
+	g_free(str);
+}
+
+static void parse_multi_profile(GKeyFile *config)
+{
+	char *str = NULL;
+
+	parse_config_string(config, "General", "MultiProfile", &str);
+	if (!str)
+		return;
+
+	if (!strcmp(str, "single"))
+		btd_opts.mps = MPS_SINGLE;
+	else if (!strcmp(str, "multiple"))
+		btd_opts.mps = MPS_MULTIPLE;
+	else
+		btd_opts.mps = MPS_OFF;
+
+	g_free(str);
+}
+
+static gboolean parse_kernel_experimental(const char *key, const char *value,
+					gpointer user_data, GError **error)
+{
+	char **strlist;
+
+	if (value && value[0] != '*') {
+		strlist = g_strsplit(value, ",", -1);
+		btd_parse_kernel_experimental(strlist);
+		g_strfreev(strlist);
+	} else {
+		if (!btd_opts.kernel)
+			btd_opts.kernel = queue_new();
+		queue_push_head(btd_opts.kernel, strdup("*"));
+	}
+
+	return TRUE;
+}
+
+static void parse_kernel_exp(GKeyFile *config)
+{
+	char *str = NULL;
+
+	if (!parse_config_string(config, "General", "KernelExperimental",
+						&str))
+		return;
+
+	parse_kernel_experimental(NULL, str, NULL, NULL);
+
+	g_free(str);
+}
+
+static void parse_secure_conns(GKeyFile *config)
+{
+	char *str = NULL;
+
+	if (!parse_config_string(config, "General", "SecureConnections",
+								&str))
+		return;
+
+	if (!strcmp(str, "off"))
+		btd_opts.secure_conn = SC_OFF;
+	else if (!strcmp(str, "on"))
+		btd_opts.secure_conn = SC_ON;
+	else if (!strcmp(str, "only"))
+		btd_opts.secure_conn = SC_ONLY;
+
+	g_free(str);
+}
+
+static void parse_general(GKeyFile *config)
+{
+	parse_config_string(config, "General", "Name", &btd_opts.name);
+	parse_config_hex(config, "General", "Class", &btd_opts.class);
+	parse_config_u32(config, "General", "DiscoverableTimeout",
+						&btd_opts.discovto,
+						0, UINT32_MAX);
+	parse_config_bool(config, "General", "AlwaysPairable",
+						&btd_opts.pairable);
+	parse_config_u32(config, "General", "PairableTimeout",
+						&btd_opts.pairto,
+						0, UINT32_MAX);
+	parse_device_id(config);
+	parse_config_bool(config, "General", "ReverseServiceDiscovery",
+						&btd_opts.reverse_discovery);
+	parse_config_bool(config, "General", "NameResolving",
+						&btd_opts.name_resolv);
+	parse_config_bool(config, "General", "DebugKeys",
+						&btd_opts.debug_keys);
+	parse_ctrl_mode(config);
+	parse_config_u16(config, "General", "MaxControllers",
+						&btd_opts.max_adapters,
+						0, UINT16_MAX);
+	parse_multi_profile(config);
+	parse_config_bool(config, "General", "FastConnectable",
+						&btd_opts.fast_conn);
+	parse_privacy(config);
+	parse_repairing(config);
+	parse_config_u32(config, "General", "TemporaryTimeout",
+						&btd_opts.tmpto,
+						0, UINT32_MAX);
+	parse_config_bool(config, "General", "RefreshDiscovery",
+						&btd_opts.refresh_discovery);
+	parse_secure_conns(config);
+	parse_config_bool(config, "General", "Experimental",
+						&btd_opts.experimental);
+	parse_kernel_exp(config);
+	parse_config_u32(config, "General", "RemoteNameRequestRetryDelay",
+					&btd_opts.name_request_retry_delay,
+					0, UINT32_MAX);
+}
+
+static void parse_gatt_cache(GKeyFile *config)
+{
+	char *str = NULL;
+
+	parse_config_string(config, "GATT", "Cache", &str);
+	if (!str)
+		return;
+
+	btd_opts.gatt_cache = parse_gatt_cache_str(str);
+	g_free(str);
+}
+
+static void parse_gatt(GKeyFile *config)
+{
+	parse_gatt_cache(config);
+	parse_config_u8(config, "GATT", "KeySize", &btd_opts.key_size, 7, 16);
+	parse_config_u16(config, "GATT", "ExchangeMTU", &btd_opts.gatt_mtu,
+				BT_ATT_DEFAULT_LE_MTU, BT_ATT_MAX_LE_MTU);
+	parse_config_u8(config, "GATT", "Channels", &btd_opts.gatt_channels,
+				1, 5);
+}
+
+static void parse_csis_sirk(GKeyFile *config)
+{
+	char *str = NULL;
+
+	if (!parse_config_string(config, "CSIS", "SIRK", &str))
+		return;
+
+	if (strlen(str) == 32 && check_sirk_alpha_numeric(str))
+		hex2bin(str, btd_opts.csis.sirk, sizeof(btd_opts.csis.sirk));
+	else if (!gen_sirk(str))
+		DBG("Unable to generate SIRK from string");
+
+	g_free(str);
+}
+
+static void parse_csis(GKeyFile *config)
+{
+	parse_csis_sirk(config);
+	parse_config_bool(config, "CSIS", "Encryption",
+					&btd_opts.csis.encrypt);
+	parse_config_u8(config, "CSIS", "Size", &btd_opts.csis.size,
+					0, UINT8_MAX);
+	parse_config_u8(config, "CSIS", "Rank", &btd_opts.csis.size,
+					0, UINT8_MAX);
+}
+
+static void parse_avdtp_session_mode(GKeyFile *config)
+{
+	char *str = NULL;
+
+	if (!parse_config_string(config, "AVDTP", "SessionMode", &str))
+		return;
+
+	if (!strcmp(str, "basic"))
+		btd_opts.avdtp.session_mode = BT_IO_MODE_BASIC;
+	else if (!strcmp(str, "ertm"))
+		btd_opts.avdtp.session_mode = BT_IO_MODE_ERTM;
+	else {
+		DBG("Invalid mode option: %s", str);
+		btd_opts.avdtp.session_mode = BT_IO_MODE_BASIC;
+	}
+
+	g_free(str);
+}
+
+static void parse_avdtp_stream_mode(GKeyFile *config)
+{
+	char *str = NULL;
+
+	if (!parse_config_string(config, "AVDTP", "StreamMode", &str))
+		return;
+
+	if (!strcmp(str, "basic"))
+		btd_opts.avdtp.stream_mode = BT_IO_MODE_BASIC;
+	else if (!strcmp(str, "streaming"))
+		btd_opts.avdtp.stream_mode = BT_IO_MODE_STREAMING;
+	else {
+		DBG("Invalid mode option: %s", str);
+		btd_opts.avdtp.stream_mode = BT_IO_MODE_BASIC;
+	}
+
+	g_free(str);
+}
+
+static void parse_avdtp(GKeyFile *config)
+{
+	parse_avdtp_session_mode(config);
+	parse_avdtp_stream_mode(config);
+}
+
+static void parse_advmon(GKeyFile *config)
+{
+	parse_config_u8(config, "AdvMon", "RSSISamplingPeriod",
+				&btd_opts.advmon.rssi_sampling_period,
+				0, UINT8_MAX);
+}
+
+static void parse_config(GKeyFile *config)
+{
 	if (!config)
 		return;
 
@@ -742,377 +1145,14 @@  static void parse_config(GKeyFile *config)
 
 	DBG("parsing %s", main_conf_file_path);
 
-	val = g_key_file_get_integer(config, "General",
-						"DiscoverableTimeout", &err);
-	if (err) {
-		DBG("%s", err->message);
-		g_clear_error(&err);
-	} else {
-		DBG("discovto=%d", val);
-		btd_opts.discovto = val;
-	}
-
-	boolean = g_key_file_get_boolean(config, "General",
-						"AlwaysPairable", &err);
-	if (err) {
-		DBG("%s", err->message);
-		g_clear_error(&err);
-	} else {
-		DBG("pairable=%s", boolean ? "true" : "false");
-		btd_opts.pairable = boolean;
-	}
-
-	val = g_key_file_get_integer(config, "General",
-						"PairableTimeout", &err);
-	if (err) {
-		DBG("%s", err->message);
-		g_clear_error(&err);
-	} else {
-		DBG("pairto=%d", val);
-		btd_opts.pairto = val;
-	}
-
-	str = g_key_file_get_string(config, "General", "Privacy", &err);
-	if (err) {
-		DBG("%s", err->message);
-		g_clear_error(&err);
-		btd_opts.privacy = 0x00;
-		btd_opts.device_privacy = true;
-	} else {
-		DBG("privacy=%s", str);
-
-		if (!strcmp(str, "network") || !strcmp(str, "on")) {
-			btd_opts.privacy = 0x01;
-		} else if (!strcmp(str, "device")) {
-			btd_opts.privacy = 0x01;
-			btd_opts.device_privacy = true;
-		} else if (!strcmp(str, "limited-network")) {
-			if (btd_opts.mode != BT_MODE_DUAL) {
-				DBG("Invalid privacy option: %s", str);
-				btd_opts.privacy = 0x00;
-			}
-			btd_opts.privacy = 0x01;
-		} else if (!strcmp(str, "limited-device")) {
-			if (btd_opts.mode != BT_MODE_DUAL) {
-				DBG("Invalid privacy option: %s", str);
-				btd_opts.privacy = 0x00;
-			}
-			btd_opts.privacy = 0x02;
-			btd_opts.device_privacy = true;
-		} else if (!strcmp(str, "off")) {
-			btd_opts.privacy = 0x00;
-			btd_opts.device_privacy = true;
-		} else {
-			DBG("Invalid privacy option: %s", str);
-			btd_opts.privacy = 0x00;
-		}
-
-		g_free(str);
-	}
-
-	str = g_key_file_get_string(config, "General",
-						"JustWorksRepairing", &err);
-	if (err) {
-		DBG("%s", err->message);
-		g_clear_error(&err);
-		btd_opts.jw_repairing = JW_REPAIRING_NEVER;
-	} else {
-		DBG("just_works_repairing=%s", str);
-		btd_opts.jw_repairing = parse_jw_repairing(str);
-		g_free(str);
-	}
-
-	val = g_key_file_get_integer(config, "General",
-						"TemporaryTimeout", &err);
-	if (err) {
-		DBG("%s", err->message);
-		g_clear_error(&err);
-	} else {
-		DBG("tmpto=%d", val);
-		btd_opts.tmpto = val;
-	}
-
-	str = g_key_file_get_string(config, "General", "Name", &err);
-	if (err) {
-		DBG("%s", err->message);
-		g_clear_error(&err);
-	} else {
-		DBG("name=%s", str);
-		g_free(btd_opts.name);
-		btd_opts.name = str;
-	}
-
-	str = g_key_file_get_string(config, "General", "Class", &err);
-	if (err) {
-		DBG("%s", err->message);
-		g_clear_error(&err);
-	} else {
-		DBG("class=%s", str);
-		btd_opts.class = strtol(str, NULL, 16);
-		g_free(str);
-	}
-
-	str = g_key_file_get_string(config, "General", "DeviceID", &err);
-	if (err) {
-		DBG("%s", err->message);
-		g_clear_error(&err);
-	} else {
-		DBG("deviceid=%s", str);
-		parse_did(str);
-		g_free(str);
-	}
-
-	boolean = g_key_file_get_boolean(config, "General",
-						"ReverseServiceDiscovery", &err);
-	if (err) {
-		DBG("%s", err->message);
-		g_clear_error(&err);
-	} else
-		btd_opts.reverse_discovery = boolean;
-
-	boolean = g_key_file_get_boolean(config, "General",
-						"NameResolving", &err);
-	if (err)
-		g_clear_error(&err);
-	else
-		btd_opts.name_resolv = boolean;
-
-	boolean = g_key_file_get_boolean(config, "General",
-						"DebugKeys", &err);
-	if (err)
-		g_clear_error(&err);
-	else
-		btd_opts.debug_keys = boolean;
-
-	str = g_key_file_get_string(config, "General", "ControllerMode", &err);
-	if (err) {
-		g_clear_error(&err);
-	} else {
-		DBG("ControllerMode=%s", str);
-		btd_opts.mode = get_mode(str);
-		g_free(str);
-	}
-
-	val = g_key_file_get_integer(config, "General", "MaxControllers", &err);
-	if (err) {
-		g_clear_error(&err);
-	} else {
-		DBG("MaxControllers=%d", val);
-		btd_opts.max_adapters = val;
-	}
-
-	str = g_key_file_get_string(config, "General", "MultiProfile", &err);
-	if (err) {
-		g_clear_error(&err);
-	} else {
-		DBG("MultiProfile=%s", str);
-
-		if (!strcmp(str, "single"))
-			btd_opts.mps = MPS_SINGLE;
-		else if (!strcmp(str, "multiple"))
-			btd_opts.mps = MPS_MULTIPLE;
-		else
-			btd_opts.mps = MPS_OFF;
-
-		g_free(str);
-	}
-
-	boolean = g_key_file_get_boolean(config, "General",
-						"FastConnectable", &err);
-	if (err)
-		g_clear_error(&err);
-	else
-		btd_opts.fast_conn = boolean;
-
-	boolean = g_key_file_get_boolean(config, "General",
-						"RefreshDiscovery", &err);
-	if (err)
-		g_clear_error(&err);
-	else
-		btd_opts.refresh_discovery = boolean;
-
-	boolean = g_key_file_get_boolean(config, "General", "Experimental",
-						&err);
-	if (err)
-		g_clear_error(&err);
-	else
-		btd_opts.experimental = boolean;
-
-	strlist = g_key_file_get_string_list(config, "General",
-						"KernelExperimental",
-						NULL, &err);
-	if (err)
-		g_clear_error(&err);
-	else {
-		btd_parse_kernel_experimental(strlist);
-		g_strfreev(strlist);
-	}
-
-	val = g_key_file_get_integer(config, "General",
-					"RemoteNameRequestRetryDelay", &err);
-	if (err) {
-		DBG("%s", err->message);
-		g_clear_error(&err);
-	} else {
-		DBG("RemoteNameRequestRetryDelay=%d", val);
-		btd_opts.name_request_retry_delay = val;
-	}
-
-	str = g_key_file_get_string(config, "General",
-						"SecureConnections", &err);
-	if (err)
-		g_clear_error(&err);
-	else {
-		if (!strcmp(str, "off"))
-			btd_opts.secure_conn = SC_OFF;
-		else if (!strcmp(str, "on"))
-			btd_opts.secure_conn = SC_ON;
-		else if (!strcmp(str, "only"))
-			btd_opts.secure_conn = SC_ONLY;
-	}
-
-	str = g_key_file_get_string(config, "GATT", "Cache", &err);
-	if (err) {
-		DBG("%s", err->message);
-		g_clear_error(&err);
-	} else {
-		btd_opts.gatt_cache = parse_gatt_cache(str);
-		g_free(str);
-	}
-
-	val = g_key_file_get_integer(config, "GATT", "KeySize", &err);
-	if (err) {
-		DBG("%s", err->message);
-		g_clear_error(&err);
-	} else {
-		DBG("KeySize=%d", val);
-
-		if (val >=7 && val <= 16)
-			btd_opts.key_size = val;
-	}
-
-	val = g_key_file_get_integer(config, "GATT", "ExchangeMTU", &err);
-	if (err) {
-		DBG("%s", err->message);
-		g_clear_error(&err);
-	} else {
-		/* Ensure the mtu is within a valid range. */
-		val = MIN(val, BT_ATT_MAX_LE_MTU);
-		val = MAX(val, BT_ATT_DEFAULT_LE_MTU);
-		DBG("ExchangeMTU=%d", val);
-		btd_opts.gatt_mtu = val;
-	}
-
-	val = g_key_file_get_integer(config, "GATT", "Channels", &err);
-	if (err) {
-		DBG("%s", err->message);
-		g_clear_error(&err);
-	} else {
-		DBG("Channels=%d", val);
-		/* Ensure the channels is within a valid range. */
-		val = MIN(val, 5);
-		val = MAX(val, 1);
-		btd_opts.gatt_channels = val;
-	}
-
-	str = g_key_file_get_string(config, "CSIS", "SIRK", &err);
-	if (err) {
-		DBG("%s", err->message);
-		g_clear_error(&err);
-	} else {
-		DBG("CSIS SIRK: %s", str);
-
-		if (strlen(str) == 32 && check_sirk_alpha_numeric(str)) {
-			hex2bin(str, btd_opts.csis.sirk,
-					sizeof(btd_opts.csis.sirk));
-		} else if (!gen_sirk(str))
-			DBG("Unable to generate SIRK from string");
-
-		g_free(str);
-	}
-
-	boolean = g_key_file_get_boolean(config, "CSIS", "Encryption", &err);
-	if (err) {
-		DBG("%s", err->message);
-		g_clear_error(&err);
-	} else {
-		DBG("CSIS Encryption: %s", boolean ? "true" : "false");
-
-		btd_opts.csis.encrypt = boolean;
-	}
-
-	val = g_key_file_get_integer(config, "CSIS", "Size", &err);
-	if (err) {
-		DBG("%s", err->message);
-		g_clear_error(&err);
-	} else {
-		val = MIN(val, 0xFF);
-		val = MAX(val, 0);
-		DBG("CSIS Size: %u", val);
-		btd_opts.csis.size = val;
-	}
-
-	val = g_key_file_get_integer(config, "CSIS", "Rank", &err);
-	if (err) {
-		DBG("%s", err->message);
-		g_clear_error(&err);
-	} else {
-		val = MIN(val, 0xFF);
-		val = MAX(val, 0);
-		DBG("CSIS Rank: %u", val);
-		btd_opts.csis.rank = val;
-	}
-
-	str = g_key_file_get_string(config, "AVDTP", "SessionMode", &err);
-	if (err) {
-		DBG("%s", err->message);
-		g_clear_error(&err);
-	} else {
-		DBG("SessionMode=%s", str);
-
-		if (!strcmp(str, "basic"))
-			btd_opts.avdtp.session_mode = BT_IO_MODE_BASIC;
-		else if (!strcmp(str, "ertm"))
-			btd_opts.avdtp.session_mode = BT_IO_MODE_ERTM;
-		else {
-			DBG("Invalid mode option: %s", str);
-			btd_opts.avdtp.session_mode = BT_IO_MODE_BASIC;
-		}
-		g_free(str);
-	}
-
-	str = g_key_file_get_string(config, "AVDTP", "StreamMode", &err);
-	if (err) {
-		DBG("%s", err->message);
-		g_clear_error(&err);
-	} else {
-		DBG("StreamMode=%s", str);
-
-		if (!strcmp(str, "basic"))
-			btd_opts.avdtp.stream_mode = BT_IO_MODE_BASIC;
-		else if (!strcmp(str, "streaming"))
-			btd_opts.avdtp.stream_mode = BT_IO_MODE_STREAMING;
-		else {
-			DBG("Invalid mode option: %s", str);
-			btd_opts.avdtp.stream_mode = BT_IO_MODE_BASIC;
-		}
-		g_free(str);
-	}
-
-	val = g_key_file_get_integer(config, "AdvMon", "RSSISamplingPeriod",
-									&err);
-	if (err) {
-		DBG("%s", err->message);
-		g_clear_error(&err);
-	} else {
-		val = MIN(val, 0xFF);
-		val = MAX(val, 0);
-		DBG("RSSISamplingPeriod=%d", val);
-		btd_opts.advmon.rssi_sampling_period = val;
-	}
-
+	/* Parse Groups */
+	parse_general(config);
 	parse_br_config(config);
 	parse_le_config(config);
+	parse_gatt(config);
+	parse_csis(config);
+	parse_avdtp(config);
+	parse_advmon(config);
 }
 
 static void init_defaults(void)
@@ -1284,24 +1324,6 @@  static gboolean parse_debug(const char *key, const char *value,
 	return TRUE;
 }
 
-static gboolean parse_kernel_experimental(const char *key, const char *value,
-					gpointer user_data, GError **error)
-{
-	char **strlist;
-
-	if (value) {
-		strlist = g_strsplit(value, ",", -1);
-		btd_parse_kernel_experimental(strlist);
-		g_strfreev(strlist);
-	} else {
-		if (!btd_opts.kernel)
-			btd_opts.kernel = queue_new();
-		queue_push_head(btd_opts.kernel, strdup("*"));
-	}
-
-	return TRUE;
-}
-
 static GOptionEntry options[] = {
 	{ "debug", 'd', G_OPTION_FLAG_OPTIONAL_ARG,
 				G_OPTION_ARG_CALLBACK, parse_debug,