[PATCH 07/15] GATT server: parse characteristics

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



---
 plugins/gatt-profile.c |  208 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 208 insertions(+), 0 deletions(-)

diff --git a/plugins/gatt-profile.c b/plugins/gatt-profile.c
index b746449..728c2ab 100644
--- a/plugins/gatt-profile.c
+++ b/plugins/gatt-profile.c
@@ -26,6 +26,7 @@
 #include <config.h>
 #endif
 
+#include <stdlib.h>
 #include <errno.h>
 
 #include <gdbus.h>
@@ -35,12 +36,36 @@
 #include "adapter.h"
 #include "error.h"
 #include "log.h"
+#include "att.h"
 
 #define GATT_PROFILE_INTERFACE "org.bluez.GattProfile"
 
 static DBusConnection *connection = NULL;
 static const char *any_path = NULL;
 
+struct gatt_descriptor {
+	uint16_t type;
+	union {
+		uint16_t ext_props;
+		uint16_t client_cfg;
+		uint16_t server_cfg;
+		gchar *description;
+		struct {
+			uint8_t format;
+			int8_t exponent;
+			uint16_t unit;
+			uint8_t namespace;
+			uint16_t description;
+		} presentation;
+	} data;
+};
+
+struct gatt_characteristic {
+	uint8_t props;
+	bt_uuid_t value_uuid;
+	GSList *descriptors;
+};
+
 struct gatt_service {
 	gboolean primary;
 	bt_uuid_t uuid;
@@ -93,6 +118,159 @@ static void parse_service(const gchar **attribute_names,
 	*services = g_slist_prepend(*services, svc);
 }
 
+static int string2uint16(uint16_t *dst, const char *src)
+{
+	int length = strlen(src);
+	char *endptr = NULL;
+
+	if (length != 4 && length != 6)
+		return -EINVAL;
+
+	*dst = strtoul(src, &endptr, 16);
+	if (endptr == NULL || *endptr != '\0')
+		return -EINVAL;
+
+	return 0;
+}
+
+static void parse_characteristic(const gchar **attribute_names,
+						const gchar **attribute_values,
+						struct gatt_service *cur_svc)
+{
+	struct gatt_characteristic *chr;
+	const gchar *uuid = NULL, *props = NULL;
+	uint16_t u16 = 0x00;
+	bt_uuid_t value_uuid;
+	int i;
+
+	for (i = 0; attribute_names[i]; i++) {
+		if (g_strcmp0(attribute_names[i], "uuid") == 0)
+			uuid = attribute_values[i];
+		else if (g_strcmp0(attribute_names[i], "properties") == 0)
+			props = attribute_values[i];
+		else
+			error("Invalid XML attribute: %s", attribute_names[i]);
+	}
+
+	if (uuid == NULL) {
+		error("Missing UUID for characteristic");
+		return;
+	}
+
+	if (bt_string_to_uuid(&value_uuid, uuid) < 0) {
+		error("Invalid UUID: %s", uuid);
+		return;
+	}
+
+	if (props != NULL && (string2uint16(&u16, props) < 0 || u16 > 0xff)) {
+		error("Invalid properties: %s", props);
+		return;
+	}
+
+	chr = g_new0(struct gatt_characteristic, 1);
+	chr->props = u16;
+	memcpy(&chr->value_uuid, &value_uuid, sizeof(chr->value_uuid));
+
+	DBG("New characteristic with UUID %s, properties 0x%02x", uuid,
+								chr->props);
+
+	cur_svc->chars = g_slist_prepend(cur_svc->chars, chr);
+}
+
+static void parse_descriptor(const gchar **attribute_names,
+				const gchar **attribute_values, uint16_t type,
+				struct gatt_service *cur_svc)
+{
+	struct gatt_characteristic *cur_chr;
+	struct gatt_descriptor dsc;
+	const gchar *fmt = NULL, *exp = NULL, *unit = NULL, *ns = NULL;
+	const gchar *desc = NULL, *value = NULL;
+	int i;
+
+	for (i = 0; attribute_names[i]; i++) {
+		if (g_strcmp0(attribute_names[i], "format") == 0)
+			fmt = attribute_values[i];
+		else if (g_strcmp0(attribute_names[i], "exponent") == 0)
+			exp = attribute_values[i];
+		else if (g_strcmp0(attribute_names[i], "unit") == 0)
+			unit = attribute_values[i];
+		else if (g_strcmp0(attribute_names[i], "namespace") == 0)
+			ns = attribute_values[i];
+		else if (g_strcmp0(attribute_names[i], "description") == 0)
+			desc = attribute_values[i];
+		else if (g_strcmp0(attribute_names[i], "value") == 0)
+			value = attribute_values[i];
+		else
+			error("Invalid XML attribute: %s", attribute_names[i]);
+	}
+
+	if (type == GATT_CHARAC_FMT_UUID && (!fmt || !unit || !ns || !desc)) {
+		error("Missing fields for presentation format");
+		return;
+	}
+
+	if (type == GATT_CHARAC_USER_DESC_UUID && value == NULL) {
+		error("Missing characteristic user description value");
+		return;
+	}
+
+	dsc.type = type;
+
+	if (type == GATT_CHARAC_USER_DESC_UUID)
+		dsc.data.description = g_strdup(value);
+	else if (type == GATT_CHARAC_FMT_UUID) {
+		uint16_t u16;
+
+		if (string2uint16(&u16, fmt) < 0 || u16 > UCHAR_MAX) {
+			error("Invalid format: %s", fmt);
+			return;
+		} else
+			dsc.data.presentation.format = u16;
+
+		/* Exponent is a signed decimal value and is optional for some
+		 * formats. */
+		if (exp) {
+			char *endptr = NULL;
+			long val = strtol(exp, &endptr, 10);
+
+			if (endptr == NULL || *endptr != '\0' ||
+					val < SCHAR_MIN || val > SCHAR_MAX) {
+				error("Invalid exponent: %s", exp);
+				return;
+			} else
+				dsc.data.presentation.exponent = val;
+		} else
+			dsc.data.presentation.exponent = 0;
+
+		if (string2uint16(&u16, unit) < 0) {
+			error("Invalid unit: %s", unit);
+			return;
+		} else
+			dsc.data.presentation.unit = u16;
+
+		if (string2uint16(&u16, ns) < 0 || u16 > UCHAR_MAX) {
+			error("Invalid namespace: %s", ns);
+			return;
+		} else
+			dsc.data.presentation.namespace = u16;
+
+		if (string2uint16(&u16, desc) < 0) {
+			error("Invalid description: %s", desc);
+			return;
+		} else
+			dsc.data.presentation.description = u16;
+	}
+
+	DBG("New %s descriptor",
+		type == GATT_CHARAC_USER_DESC_UUID ? "user description" :
+		type == GATT_CHARAC_FMT_UUID ? "presentation format" :
+		"unknown");
+
+	cur_chr = cur_svc->chars->data;
+	cur_chr->descriptors = g_slist_prepend(cur_chr->descriptors,
+						g_memdup(&dsc, sizeof(dsc)));
+}
+
 static void parse_include(const gchar **attribute_names,
 						const gchar **attribute_values,
 						struct gatt_service *cur_svc)
@@ -135,6 +313,16 @@ static void element_start(GMarkupParseContext *ctx, const gchar *element_name,
 		if (cur_svc == NULL)
 			error("XML tag \"%s\" must be inside a service",
 								element_name);
+		else if (g_strcmp0(element_name, "characteristic") == 0)
+			parse_characteristic(attribute_names, attribute_values,
+								cur_svc);
+		else if (g_strcmp0(element_name, "presentation") == 0)
+			parse_descriptor(attribute_names, attribute_values,
+						GATT_CHARAC_FMT_UUID, cur_svc);
+		else if (g_strcmp0(element_name, "user-description") == 0)
+			parse_descriptor(attribute_names, attribute_values,
+						GATT_CHARAC_USER_DESC_UUID,
+						cur_svc);
 		else if (g_strcmp0(element_name, "include") == 0)
 			parse_include(attribute_names, attribute_values,
 								cur_svc);
@@ -185,6 +373,26 @@ static void resolve_includes(gpointer data, gpointer user_data)
 static void free_services(gpointer data, gpointer user_data)
 {
 	struct gatt_service *svc = data;
+	GSList *cl;
+
+	for (cl = svc->chars; cl; cl = cl->next) {
+		struct gatt_characteristic *chr = cl->data;
+		GSList *dl;
+
+		for (dl = chr->descriptors; dl; dl = dl->next) {
+			struct gatt_descriptor *dsc = dl->data;
+
+			if (dsc->type == GATT_CHARAC_USER_DESC_UUID)
+				g_free(dsc->data.description);
+
+			g_free(dsc);
+		}
+
+		g_slist_free(chr->descriptors);
+		g_free(chr);
+	}
+
+	g_slist_free(svc->chars);
 
 	g_free(svc->id);
 	g_slist_free(svc->includes);
-- 
1.7.0.4

--
To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Bluez Devel]     [Linux Wireless Networking]     [Linux Wireless Personal Area Networking]     [Linux ATH6KL]     [Linux USB Devel]     [Linux Media Drivers]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Big List of Linux Books]

  Powered by Linux