2011-10-20 09:46:43

by Santiago Carot

[permalink] [raw]
Subject: Health Thermometer Profile

This set of patches enable applications to register watchers in order to
receive final measurements.

[PATCH 1/8] Process characteristic descriptors in thermometer service.
[PATCH 2/8] Get valid range descriptor if it is supported
[PATCH 3/8] Implement D-Bus register watcher function
[PATCH 4/8] Implement D-Bus unregister watcher function
[PATCH 5/8] Enable final measurement indications when first watcher
[PATCH 6/8] Disable final measurements indication when last watcher
[PATCH 7/8] Manage watcher's disconnections from the bus
[PATCH 8/8] Implement D-Bus get properties function


2011-10-20 11:11:18

by Anderson Lizardo

[permalink] [raw]
Subject: Re: [PATCH 1/8] Process characteristic descriptors in thermometer service.

Hi Santiago,

On Thu, Oct 20, 2011 at 6:46 AM, Santiago Carot-Nemesio
<[email protected]> wrote:
> + ? ? ? ? ? ? ? } else
> + ? ? ? ? ? ? ? ? ? ? ? goto end;

We usually call this label "done".

> +
> + ? ? ? ? ? ? ? return;
> + ? ? ? }
> +
> + ? ? ? bt_uuid16_create(&btuuid, GATT_CHARAC_VALID_RANGE_UUID);
> +
> + ? ? ? if (bt_uuid_cmp(&desc->uuid, &btuuid) == 0)
> + ? ? ? ? ? ? ? if (g_strcmp0(desc->ch->attr.uuid,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? MEASUREMENT_INTERVAL_UUID) == 0) {

You can join these nested if()'s in a single one and use &&.

Regards,
--
Anderson Lizardo
Instituto Nokia de Tecnologia - INdT
Manaus - Brazil

2011-10-20 09:46:46

by Santiago Carot

[permalink] [raw]
Subject: [PATCH 3/8] Implement D-Bus register watcher function

---
thermometer/thermometer.c | 96 +++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 93 insertions(+), 3 deletions(-)

diff --git a/thermometer/thermometer.c b/thermometer/thermometer.c
index 343d72a..05c6ef6 100644
--- a/thermometer/thermometer.c
+++ b/thermometer/thermometer.c
@@ -55,6 +55,7 @@ struct thermometer {
guint attioid; /* Att watcher id */
guint attindid; /* Att incications id */
GSList *chars; /* Characteristics */
+ GSList *fwatchers; /* Final measurements */
gboolean intermediate;
guint8 type;
guint16 interval;
@@ -76,8 +77,27 @@ struct descriptor {
bt_uuid_t uuid;
};

+struct watcher {
+ struct thermometer *t;
+ guint id;
+ gchar *srv;
+ gchar *path;
+};
+
static GSList *thermometers = NULL;

+static void destroy_watcher(gpointer user_data)
+{
+ struct watcher *watcher = user_data;
+
+ if (watcher->id > 0)
+ g_dbus_remove_watch(watcher->t->conn, watcher->id);
+
+ g_free(watcher->path);
+ g_free(watcher->srv);
+ g_free(watcher);
+}
+
static void destroy_char(gpointer user_data)
{
struct characteristic *c = user_data;
@@ -102,6 +122,9 @@ static void destroy_thermometer(gpointer user_data)
if (t->chars != NULL)
g_slist_free_full(t->chars, destroy_char);

+ if (t->fwatchers != NULL)
+ g_slist_free_full(t->fwatchers, destroy_watcher);
+
dbus_connection_unref(t->conn);
btd_device_unref(t->dev);
g_free(t->svc_range);
@@ -119,6 +142,19 @@ static gint cmp_device(gconstpointer a, gconstpointer b)
return -1;
}

+static gint cmp_watcher(gconstpointer a, gconstpointer b)
+{
+ const struct watcher *watcher = a;
+ const struct watcher *match = b;
+ int ret;
+
+ ret = g_strcmp0(watcher->srv, match->srv);
+ if (ret != 0)
+ return ret;
+
+ return g_strcmp0(watcher->path, match->path);
+}
+
static void change_property(struct thermometer *t, const gchar *name,
gpointer value) {
if (g_strcmp0(name, "Intermediate") == 0) {
@@ -413,12 +449,66 @@ static DBusMessage *set_property(DBusConnection *conn, DBusMessage *msg,
"Function not implemented.");
}

+static void enable_final_measurement(struct thermometer *t)
+{
+ /* TODO: enable final measurements */
+}
+
+static void watcher_exit(DBusConnection *conn, void *user_data)
+{
+ /* TODO: Watcher disconnected */
+}
+
+static struct watcher *find_watcher(struct thermometer *t, const gchar *sender,
+ const gchar *path)
+{
+ struct watcher *match;
+ GSList *l;
+
+ match = g_new0(struct watcher, 1);
+ match->srv = g_strdup(sender);
+ match->path = g_strdup(path);
+
+ l = g_slist_find_custom(t->fwatchers, match, cmp_watcher);
+ destroy_watcher(match);
+
+ if (l != NULL)
+ return l->data;
+
+ return NULL;
+}
+
static DBusMessage *register_watcher(DBusConnection *conn, DBusMessage *msg,
void *data)
{
- /* TODO: */
- return g_dbus_create_error(msg, ERROR_INTERFACE ".ThermometerError",
- "Function not implemented.");
+ const gchar *sender = dbus_message_get_sender(msg);
+ struct thermometer *t = data;
+ struct watcher *watcher;
+ gchar *path;
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID))
+ return btd_error_invalid_args(msg);
+
+ watcher = find_watcher(t, sender, path);
+ if (watcher != NULL)
+ return btd_error_already_exists(msg);
+
+ DBG("Thermometer watcher %s registered", path);
+
+ watcher = g_new0(struct watcher, 1);
+ watcher->srv = g_strdup(sender);
+ watcher->path = g_strdup(path);
+ watcher->t = t;
+ watcher->id = g_dbus_add_disconnect_watch(conn, sender, watcher_exit,
+ watcher, destroy_watcher);
+
+ if (g_slist_length(t->fwatchers) == 0)
+ enable_final_measurement(t);
+
+ t->fwatchers = g_slist_prepend(t->fwatchers, watcher);
+
+ return dbus_message_new_method_return(msg);
}

static DBusMessage *unregister_watcher(DBusConnection *conn, DBusMessage *msg,
--
1.7.6.1


2011-10-20 09:46:51

by Santiago Carot

[permalink] [raw]
Subject: [PATCH 8/8] Implement D-Bus get properties function

---
thermometer/thermometer.c | 32 +++++++++++++++++++++++++++++---
1 files changed, 29 insertions(+), 3 deletions(-)

diff --git a/thermometer/thermometer.c b/thermometer/thermometer.c
index 4a2b9c8..99bfe43 100644
--- a/thermometer/thermometer.c
+++ b/thermometer/thermometer.c
@@ -452,9 +452,35 @@ static void configure_thermometer_cb(GSList *characteristics, guint8 status,
static DBusMessage *get_properties(DBusConnection *conn, DBusMessage *msg,
void *data)
{
- /* TODO: */
- return g_dbus_create_error(msg, ERROR_INTERFACE ".ThermometerError",
- "Function not implemented.");
+ struct thermometer *t = data;
+ DBusMessageIter iter;
+ DBusMessageIter dict;
+ DBusMessage *reply;
+
+ reply = dbus_message_new_method_return(msg);
+ if (reply == NULL)
+ return NULL;
+
+ dbus_message_iter_init_append(reply, &iter);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+ dict_append_entry(&dict, "Intermediate", DBUS_TYPE_BOOLEAN,
+ &t->intermediate);
+
+ if (t->has_interval) {
+ dict_append_entry(&dict, "Interval", DBUS_TYPE_UINT16,
+ &t->interval);
+ dict_append_entry(&dict, "Maximum", DBUS_TYPE_UINT16, &t->max);
+ dict_append_entry(&dict, "Minimum", DBUS_TYPE_UINT16, &t->min);
+ }
+
+ dbus_message_iter_close_container(&iter, &dict);
+
+ return reply;
}

static DBusMessage *set_property(DBusConnection *conn, DBusMessage *msg,
--
1.7.6.1


2011-10-20 09:46:44

by Santiago Carot

[permalink] [raw]
Subject: [PATCH 1/8] Process characteristic descriptors in thermometer service.

---
thermometer/thermometer.c | 77 ++++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 76 insertions(+), 1 deletions(-)

diff --git a/thermometer/thermometer.c b/thermometer/thermometer.c
index 0d85102..854a157 100644
--- a/thermometer/thermometer.c
+++ b/thermometer/thermometer.c
@@ -42,6 +42,7 @@

#define THERMOMETER_INTERFACE "org.bluez.Thermometer"

+#define TEMPERATURE_MEASUREMENT_UUID "00002a1c-0000-1000-8000-00805f9b34fb"
#define TEMPERATURE_TYPE_UUID "00002a1d-0000-1000-8000-00805f9b34fb"
#define INTERMEDIATE_TEMPERATURE_UUID "00002a1e-0000-1000-8000-00805f9b34fb"
#define MEASUREMENT_INTERVAL_UUID "00002a21-0000-1000-8000-00805f9b34fb"
@@ -161,10 +162,84 @@ static void change_property(struct thermometer *t, const gchar *name,
DBG("%s is not a thermometer property", name);
}

+static void process_thermometer_desc(struct descriptor *desc)
+{
+ char uuidstr[MAX_LEN_UUID_STR];
+ bt_uuid_t btuuid;
+
+ bt_uuid16_create(&btuuid, GATT_CLIENT_CHARAC_CFG_UUID);
+
+ if (bt_uuid_cmp(&desc->uuid, &btuuid) == 0) {
+ if (g_strcmp0(desc->ch->attr.uuid,
+ TEMPERATURE_MEASUREMENT_UUID) == 0) {
+ /* TODO: Check if we have to enable it */
+ DBG("C.C.C in Temperature Measurement");
+ } else if (g_strcmp0(desc->ch->attr.uuid,
+ INTERMEDIATE_TEMPERATURE_UUID) == 0) {
+ /* TODO: Check if we have to enable it */
+ DBG("C.C.C in Intermediate Temperature");
+ } else if (g_strcmp0(desc->ch->attr.uuid,
+ MEASUREMENT_INTERVAL_UUID) == 0) {
+ /* TODO: Enable indications */
+ DBG("C.C.C in Measurement Interval");
+ } else
+ goto end;
+
+ return;
+ }
+
+ bt_uuid16_create(&btuuid, GATT_CHARAC_VALID_RANGE_UUID);
+
+ if (bt_uuid_cmp(&desc->uuid, &btuuid) == 0)
+ if (g_strcmp0(desc->ch->attr.uuid,
+ MEASUREMENT_INTERVAL_UUID) == 0) {
+ /* TODO: Process Measurement Interval */
+ return;
+ }
+
+end:
+ bt_uuid_to_string(&desc->uuid, uuidstr, MAX_LEN_UUID_STR);
+ DBG("Ignored descriptor %s in characteristic %s", uuidstr,
+ desc->ch->attr.uuid);
+}
+
static void discover_desc_cb(guint8 status, const guint8 *pdu, guint16 len,
gpointer user_data)
{
- /* TODO */
+ struct characteristic *ch = user_data;
+ struct att_data_list *list;
+ guint8 format;
+ int i;
+
+ if (status != 0) {
+ error("Discover all characteristic descriptors failed [%s]: %s",
+ ch->attr.uuid, att_ecode2str(status));
+ return;
+ }
+
+ list = dec_find_info_resp(pdu, len, &format);
+ if (list == NULL)
+ return;
+
+ for (i = 0; i < list->num; i++) {
+ struct descriptor *desc;
+ uint8_t *value;
+
+ value = list->data[i];
+ desc = g_new0(struct descriptor, 1);
+ desc->handle = att_get_u16(value);
+ desc->ch = ch;
+
+ if (format == 0x01)
+ desc->uuid = att_get_uuid16(&value[2]);
+ else
+ desc->uuid = att_get_uuid128(&value[2]);
+
+ ch->desc = g_slist_append(ch->desc, desc);
+ process_thermometer_desc(desc);
+ }
+
+ att_data_list_free(list);
}

static void read_temp_type_cb(guint8 status, const guint8 *pdu, guint16 len,
--
1.7.6.1


2011-10-20 09:46:45

by Santiago Carot

[permalink] [raw]
Subject: [PATCH 2/8] Get valid range descriptor if it is supported

---
thermometer/thermometer.c | 41 ++++++++++++++++++++++++++++++++++++++++-
1 files changed, 40 insertions(+), 1 deletions(-)

diff --git a/thermometer/thermometer.c b/thermometer/thermometer.c
index 854a157..343d72a 100644
--- a/thermometer/thermometer.c
+++ b/thermometer/thermometer.c
@@ -162,6 +162,44 @@ static void change_property(struct thermometer *t, const gchar *name,
DBG("%s is not a thermometer property", name);
}

+static void valid_range_desc_cb(guint8 status, const guint8 *pdu, guint16 len,
+ gpointer user_data)
+{
+ struct descriptor *desc = user_data;
+ uint8_t value[ATT_MAX_MTU];
+ uint16_t *p, max, min;
+ int vlen;
+
+ if (status != 0) {
+ DBG("Valid Range descriptor read failed: %s",
+ att_ecode2str(status));
+ return;
+ }
+
+ if (!dec_read_resp(pdu, len, value, &vlen)) {
+ DBG("Protocol error\n");
+ return;
+ }
+
+ if (vlen < 4) {
+ DBG("Invalid range received");
+ return;
+ }
+
+ p = (uint16_t *) value;
+ min = btohs(bt_get_unaligned(p));
+ p = (uint16_t *) &value[2];
+ max = btohs(bt_get_unaligned(p));
+
+ if (min == 0 || min > max) {
+ DBG("Invalid range");
+ return;
+ }
+
+ change_property(desc->ch->t, "Maximum", &max);
+ change_property(desc->ch->t, "Minimum", &min);
+}
+
static void process_thermometer_desc(struct descriptor *desc)
{
char uuidstr[MAX_LEN_UUID_STR];
@@ -193,7 +231,8 @@ static void process_thermometer_desc(struct descriptor *desc)
if (bt_uuid_cmp(&desc->uuid, &btuuid) == 0)
if (g_strcmp0(desc->ch->attr.uuid,
MEASUREMENT_INTERVAL_UUID) == 0) {
- /* TODO: Process Measurement Interval */
+ gatt_read_char(desc->ch->t->attrib, desc->handle, 0,
+ valid_range_desc_cb, desc);
return;
}

--
1.7.6.1


2011-10-20 09:46:49

by Santiago Carot

[permalink] [raw]
Subject: [PATCH 6/8] Disable final measurements indication when last watcher is removed

---
thermometer/thermometer.c | 25 ++++++++++++++++++++++++-
1 files changed, 24 insertions(+), 1 deletions(-)

diff --git a/thermometer/thermometer.c b/thermometer/thermometer.c
index 4a03fc6..9b14e6f 100644
--- a/thermometer/thermometer.c
+++ b/thermometer/thermometer.c
@@ -532,7 +532,30 @@ static void enable_final_measurement(struct thermometer *t)

static void disable_final_measurement(struct thermometer *t)
{
- /* TODO: disable final measurements */
+ struct characteristic *ch;
+ struct descriptor *desc;
+ bt_uuid_t btuuid;
+ uint8_t atval[2];
+ gchar *msg;
+
+ ch = get_characteristic(t, TEMPERATURE_MEASUREMENT_UUID);
+ if (ch == NULL) {
+ DBG("Temperature measurement characteristic not found");
+ return;
+ }
+
+ bt_uuid16_create(&btuuid, GATT_CLIENT_CHARAC_CFG_UUID);
+ desc = get_descriptor(ch, &btuuid);
+ if (desc == NULL) {
+ DBG("Client characteristic configuration descriptor not found");
+ return;
+ }
+
+ atval[0] = 0x00;
+ atval[1] = 0x00;
+ msg = g_strdup("Disable final measurement");
+ gatt_write_char(t->attrib, desc->handle, atval, 2,
+ final_measurement_cb, msg);
}

static void watcher_exit(DBusConnection *conn, void *user_data)
--
1.7.6.1


2011-10-20 09:46:50

by Santiago Carot

[permalink] [raw]
Subject: [PATCH 7/8] Manage watcher's disconnections from the bus

---
thermometer/thermometer.c | 11 ++++++++++-
1 files changed, 10 insertions(+), 1 deletions(-)

diff --git a/thermometer/thermometer.c b/thermometer/thermometer.c
index 9b14e6f..4a2b9c8 100644
--- a/thermometer/thermometer.c
+++ b/thermometer/thermometer.c
@@ -560,7 +560,16 @@ static void disable_final_measurement(struct thermometer *t)

static void watcher_exit(DBusConnection *conn, void *user_data)
{
- /* TODO: Watcher disconnected */
+ struct watcher *watcher = user_data;
+ struct thermometer *t = watcher->t;
+
+ DBG("Thermometer watcher %s disconnected", watcher->path);
+
+ t->fwatchers = g_slist_remove(t->fwatchers, watcher);
+ watcher->id = 0;
+
+ if (g_slist_length(t->fwatchers) == 0)
+ disable_final_measurement(t);
}

static struct watcher *find_watcher(struct thermometer *t, const gchar *sender,
--
1.7.6.1


2011-10-20 09:46:48

by Santiago Carot

[permalink] [raw]
Subject: [PATCH 5/8] Enable final measurement indications when first watcher is enabled

---
thermometer/thermometer.c | 78 ++++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 77 insertions(+), 1 deletions(-)

diff --git a/thermometer/thermometer.c b/thermometer/thermometer.c
index d6afe17..4a03fc6 100644
--- a/thermometer/thermometer.c
+++ b/thermometer/thermometer.c
@@ -155,6 +155,22 @@ static gint cmp_watcher(gconstpointer a, gconstpointer b)
return g_strcmp0(watcher->path, match->path);
}

+static gint cmp_char_uuid(gconstpointer a, gconstpointer b)
+{
+ const struct characteristic *ch = a;
+ const gchar *uuid = b;
+
+ return g_strcmp0(ch->attr.uuid, uuid);
+}
+
+static gint cmp_descriptor(gconstpointer a, gconstpointer b)
+{
+ const struct descriptor *desc = a;
+ const bt_uuid_t *uuid = b;
+
+ return bt_uuid_cmp(&desc->uuid, uuid);
+}
+
static void change_property(struct thermometer *t, const gchar *name,
gpointer value) {
if (g_strcmp0(name, "Intermediate") == 0) {
@@ -449,9 +465,69 @@ static DBusMessage *set_property(DBusConnection *conn, DBusMessage *msg,
"Function not implemented.");
}

+static struct characteristic *get_characteristic(struct thermometer *t,
+ const gchar *uuid)
+{
+ GSList *l;
+
+ l = g_slist_find_custom(t->chars, uuid, cmp_char_uuid);
+ if (l == NULL)
+ return NULL;
+
+ return l->data;
+}
+
+static struct descriptor *get_descriptor(struct characteristic *ch,
+ const bt_uuid_t *uuid)
+{
+ GSList *l;
+
+ l = g_slist_find_custom(ch->desc, uuid, cmp_descriptor);
+ if (l == NULL)
+ return NULL;
+
+ return l->data;
+}
+
+static void final_measurement_cb (guint8 status, const guint8 *pdu,
+ guint16 len, gpointer user_data)
+{
+ gchar *msg = user_data;
+
+ if (status != 0) {
+ error("%s failed", msg);
+ return;
+ }
+
+ g_free(msg);
+}
+
static void enable_final_measurement(struct thermometer *t)
{
- /* TODO: enable final measurements */
+ struct characteristic *ch;
+ struct descriptor *desc;
+ bt_uuid_t btuuid;
+ uint8_t atval[2];
+ gchar *msg;
+
+ ch = get_characteristic(t, TEMPERATURE_MEASUREMENT_UUID);
+ if (ch == NULL) {
+ DBG("Temperature measurement characteristic not found");
+ return;
+ }
+
+ bt_uuid16_create(&btuuid, GATT_CLIENT_CHARAC_CFG_UUID);
+ desc = get_descriptor(ch, &btuuid);
+ if (desc == NULL) {
+ DBG("Client characteristic configuration descriptor not found");
+ return;
+ }
+
+ atval[0] = 0x02;
+ atval[1] = 0x00;
+ msg = g_strdup("Enable final measurement");
+ gatt_write_char(t->attrib, desc->handle, atval, 2,
+ final_measurement_cb, msg);
}

static void disable_final_measurement(struct thermometer *t)
--
1.7.6.1


2011-10-20 09:46:47

by Santiago Carot

[permalink] [raw]
Subject: [PATCH 4/8] Implement D-Bus unregister watcher function

---
thermometer/thermometer.c | 30 +++++++++++++++++++++++++++---
1 files changed, 27 insertions(+), 3 deletions(-)

diff --git a/thermometer/thermometer.c b/thermometer/thermometer.c
index 05c6ef6..d6afe17 100644
--- a/thermometer/thermometer.c
+++ b/thermometer/thermometer.c
@@ -454,6 +454,11 @@ static void enable_final_measurement(struct thermometer *t)
/* TODO: enable final measurements */
}

+static void disable_final_measurement(struct thermometer *t)
+{
+ /* TODO: disable final measurements */
+}
+
static void watcher_exit(DBusConnection *conn, void *user_data)
{
/* TODO: Watcher disconnected */
@@ -514,9 +519,28 @@ static DBusMessage *register_watcher(DBusConnection *conn, DBusMessage *msg,
static DBusMessage *unregister_watcher(DBusConnection *conn, DBusMessage *msg,
void *data)
{
- /* TODO: */
- return g_dbus_create_error(msg, ERROR_INTERFACE ".ThermometerError",
- "Function not implemented.");
+ const gchar *sender = dbus_message_get_sender(msg);
+ struct thermometer *t = data;
+ struct watcher *watcher;
+ gchar *path;
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID))
+ return btd_error_invalid_args(msg);
+
+ watcher = find_watcher(t, sender, path);
+ if (watcher == NULL)
+ return btd_error_does_not_exist(msg);
+
+ DBG("Thermometer watcher %s unregistered", path);
+
+ t->fwatchers = g_slist_remove(t->fwatchers, watcher);
+ destroy_watcher(watcher);
+
+ if (g_slist_length(t->fwatchers) == 0)
+ disable_final_measurement(t);
+
+ return dbus_message_new_method_return(msg);
}

static DBusMessage *enable_intermediate(DBusConnection *conn, DBusMessage *msg,
--
1.7.6.1