Linux bluetooth development
 help / color / mirror / Atom feed
From: Anderson Lizardo <anderson.lizardo@openbossa.org>
To: linux-bluetooth@vger.kernel.org
Cc: Anderson Lizardo <anderson.lizardo@openbossa.org>
Subject: [PATCH 07/15] GATT server: parse characteristics
Date: Wed, 16 Mar 2011 21:00:31 -0400	[thread overview]
Message-ID: <1300323639-13296-8-git-send-email-anderson.lizardo@openbossa.org> (raw)
In-Reply-To: <1300323639-13296-1-git-send-email-anderson.lizardo@openbossa.org>

---
 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


  parent reply	other threads:[~2011-03-17  1:00 UTC|newest]

Thread overview: 16+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2011-03-17  1:00 [RFC][PATCH 00/15] Attribute server management API Anderson Lizardo
2011-03-17  1:00 ` [PATCH 01/15] Remove built-in example attribute server Anderson Lizardo
2011-03-17  1:00 ` [PATCH 02/15] GATT server: add profile API plugin skeleton Anderson Lizardo
2011-03-17  1:00 ` [PATCH 03/15] GATT server: add initial D-Bus interface Anderson Lizardo
2011-03-17  1:00 ` [PATCH 04/15] GATT server: add initial XML parsing Anderson Lizardo
2011-03-17  1:00 ` [PATCH 05/15] GATT server: parse primary/secondary services Anderson Lizardo
2011-03-17  1:00 ` [PATCH 06/15] GATT server: parse and resolve service includes Anderson Lizardo
2011-03-17  1:00 ` Anderson Lizardo [this message]
2011-03-17  1:00 ` [PATCH 08/15] GATT server: parse characteristic value Anderson Lizardo
2011-03-17  1:00 ` [PATCH 09/15] GATT server: add test/test-gatt-profile example Anderson Lizardo
2011-03-17  1:00 ` [PATCH 10/15] Add attrib_db_find_avail() function to attribute server Anderson Lizardo
2011-03-17  1:00 ` [PATCH 11/15] GATT server: add service registration Anderson Lizardo
2011-03-17  1:00 ` [PATCH 12/15] GATT server: add service includes registration Anderson Lizardo
2011-03-17  1:00 ` [PATCH 13/15] GATT server: add characteristic registration Anderson Lizardo
2011-03-17  1:00 ` [PATCH 14/15] GATT server: add characteristic descriptor registration Anderson Lizardo
2011-03-17  1:00 ` [PATCH 15/15] GATT server: add disconnect watch Anderson Lizardo

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1300323639-13296-8-git-send-email-anderson.lizardo@openbossa.org \
    --to=anderson.lizardo@openbossa.org \
    --cc=linux-bluetooth@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox