Hi,
Here's v6 of heartrate plugin patches. After some thinking I decided to
refactor code a bit to keep it more compact and (hopefully) easier to
follow. Most important change here is that instead of nested structures
to hold all characteristics and descriptors I only store handles to few
attributes which are used later. This allows to remove some code lines,
mostly helper functions to traverse lists of structures in order to get
some attribute handle which is now accessible immediately.
So to sum up v5 vs. v6: less code, less patches, still works.
Comments are welcome.
Andrzej Kaczmarek (6):
heartrate: Add attio callbacks
heartrate: Discover HRS characteristics
heartrate: Discover Heart Rate Measurement CCC
heartrate: Add support to enable notifications
heartrate: Process measurement notifications
heartrate: Add support to reset Energy Expended
Rafal Garbat (6):
heartrate: Add initial HRP client support
heartrate: Read Body Sensor Location characteristics
heartrate: Add HeartRateManager interface
heartrate: Add GetProperties method
heartrate: Add HeartRateWatcher interface to default policy
heartrate: Add test script
Santiago Carot-Nemesio (1):
Heart Rate Profile (HRP) client API
Makefile.am | 9 +-
Makefile.tools | 4 +-
doc/heartrate-api.txt | 84 +++++
lib/uuid.h | 5 +
profiles/heartrate/heartrate.c | 826 +++++++++++++++++++++++++++++++++++++++++
profiles/heartrate/heartrate.h | 27 ++
profiles/heartrate/main.c | 52 +++
profiles/heartrate/manager.c | 93 +++++
profiles/heartrate/manager.h | 24 ++
src/bluetooth.conf | 1 +
test/test-heartrate | 103 +++++
11 files changed, 1224 insertions(+), 4 deletions(-)
create mode 100644 doc/heartrate-api.txt
create mode 100644 profiles/heartrate/heartrate.c
create mode 100644 profiles/heartrate/heartrate.h
create mode 100644 profiles/heartrate/main.c
create mode 100644 profiles/heartrate/manager.c
create mode 100644 profiles/heartrate/manager.h
create mode 100755 test/test-heartrate
--
1.7.11.3
Hi Andrzej,
On Thu, Sep 20, 2012, Andrzej Kaczmarek wrote:
> +static const char * const location_enum[] = {
> + "Other",
> + "Chest",
> + "Wrist",
> + "Finger",
> + "Hand",
> + "Earlobe",
> + "Foot"
> +};
Please add a "," to the end of the last entry to make patches that add
new entries look nicer (they don't have to first remove the last line
and then add two new lines).
Also, is the "const * const" stuff really needed? I think in most places
in the code base with tables like this we've just left out the second
one.
Johan
Hi Andrzej,
On Thu, Sep 20, 2012, Andrzej Kaczmarek wrote:
> @@ -238,6 +249,18 @@ static void discover_ccc_cb(guint8 status, const guint8 *pdu,
>
> if (uuid == GATT_CLIENT_CHARAC_CFG_UUID) {
> hr->measurement_ccc_handle = handle;
> +
> + if (g_slist_length(hr->hradapter->watchers) > 0) {
> + uint8_t value[2];
> + char *msg;
> +
> + att_put_u16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT,
> + value);
> + msg = g_strdup("Enable measurement");
> +
> + gatt_write_char(hr->attrib, handle, value,
> + sizeof(value), char_write_cb, msg);
> + }
> break;
> }
Seems like you could avoid the extra indentation above by inverting the
g_slist_length test:
if (g_slist_length(hr->hradapter->watchers) == 0)
break;
...
break;
> g_dbus_remove_watch(conn, watcher->id);
> +
> +
> + if (g_slist_length(hradapter->watchers) == 0)
Two consecutive empty lines should never be needed. Please remove one.
Johan
Hi Andrzej,
On Thu, Sep 20, 2012, Andrzej Kaczmarek wrote:
> This patch adds support for org.bluez.HeartRateManager interface on adapters
> which allows to register and unregister per-adapter watcher.
> ---
> profiles/heartrate/heartrate.c | 154 ++++++++++++++++++++++++++++++++++++++++-
> 1 file changed, 153 insertions(+), 1 deletion(-)
This one will need to be redone due to the btd_get_dbus_connection
patches that went in.
Also, please go through all your commit messages and ensure that your
line widths are less than 74 (the summary line needs to be max 72 due to
"git shortlog" indenting by 8 characters so for simplicity you could
just configure your editor to use 72 for all commit messages).
Johan
Hi Andrzej,
On Thu, Sep 20, 2012, Andrzej Kaczmarek wrote:
> +static void read_sensor_location_cb(guint8 status, const guint8 *pdu,
> + guint16 len, gpointer user_data)
> +{
> + struct heartrate *hr = user_data;
> + uint8_t value;
> + ssize_t vlen;
> +
> + if (status != 0) {
> + error("Body Sensor Location read failed: %s",
> + att_ecode2str(status));
> + return;
> + }
> +
> + vlen = dec_read_resp(pdu, len, &value, sizeof(value));
> + if (vlen < 0) {
> + error("Protocol error");
> + return;
> + }
> +
> + if (vlen != 1) {
> + error("Invalid length for Body Sensor Location");
> + return;
> + }
Minor nitpick: vlen != sizeof(value) would make it more clear exactly
what you want to compare with.
Johan
From: Rafal Garbat <[email protected]>
---
profiles/heartrate/heartrate.c | 61 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 61 insertions(+)
diff --git a/profiles/heartrate/heartrate.c b/profiles/heartrate/heartrate.c
index 21ec1f7..e00789f 100644
--- a/profiles/heartrate/heartrate.c
+++ b/profiles/heartrate/heartrate.c
@@ -94,6 +94,25 @@ struct measurement {
static GSList *heartrate_adapters = NULL;
+static const char * const location_enum[] = {
+ "Other",
+ "Chest",
+ "Wrist",
+ "Finger",
+ "Hand",
+ "Earlobe",
+ "Foot"
+};
+
+static const gchar *location2str(uint8_t value)
+{
+ if (value < G_N_ELEMENTS(location_enum))
+ return location_enum[value];
+
+ error("Body Sensor Location [%d] is RFU", value);
+ return NULL;
+}
+
static gint cmp_adapter(gconstpointer a, gconstpointer b)
{
const struct heartrate_adapter *hradapter = a;
@@ -629,6 +648,45 @@ static const GDBusMethodTable heartrate_manager_methods[] = {
{ }
};
+static DBusMessage *get_properties(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct heartrate *hr = data;
+ DBusMessageIter iter;
+ DBusMessageIter dict;
+ DBusMessage *reply;
+ gboolean has_reset;
+
+ 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);
+
+ if (hr->has_location) {
+ char *loc = g_strdup(location2str(hr->location));
+
+ if (loc) {
+ dict_append_entry(&dict, "Location",
+ DBUS_TYPE_STRING, &loc);
+ g_free(loc);
+ }
+ }
+
+ has_reset = !!hr->hrcp_val_handle;
+ dict_append_entry(&dict, "ResetSupported", DBUS_TYPE_BOOLEAN,
+ &has_reset);
+
+ dbus_message_iter_close_container(&iter, &dict);
+
+ return reply;
+}
+
static DBusMessage *hrcp_reset(DBusConnection *conn, DBusMessage *msg,
void *data)
{
@@ -653,6 +711,9 @@ static DBusMessage *hrcp_reset(DBusConnection *conn, DBusMessage *msg,
}
static const GDBusMethodTable heartrate_device_methods[] = {
+ { GDBUS_METHOD("GetProperties",
+ NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
+ get_properties) },
{ GDBUS_METHOD("Reset", NULL, NULL,
hrcp_reset) },
{ }
--
1.7.11.3
This patch adds Reset method on HeartRate interface to reset Energy Expended.
---
profiles/heartrate/heartrate.c | 45 +++++++++++++++++++++++++++++++++++++++++-
1 file changed, 44 insertions(+), 1 deletion(-)
diff --git a/profiles/heartrate/heartrate.c b/profiles/heartrate/heartrate.c
index 2e8e896..21ec1f7 100644
--- a/profiles/heartrate/heartrate.c
+++ b/profiles/heartrate/heartrate.c
@@ -41,6 +41,7 @@
#include "log.h"
#include "heartrate.h"
+#define HEART_RATE_INTERFACE "org.bluez.HeartRate"
#define HEART_RATE_MANAGER_INTERFACE "org.bluez.HeartRateManager"
#define HEART_RATE_WATCHER_INTERFACE "org.bluez.HeartRateWatcher"
@@ -628,6 +629,35 @@ static const GDBusMethodTable heartrate_manager_methods[] = {
{ }
};
+static DBusMessage *hrcp_reset(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct heartrate *hr = data;
+ uint8_t value;
+ char *vmsg;
+
+ if (!hr->hrcp_val_handle)
+ return btd_error_not_supported(msg);
+
+ if (!hr->attrib)
+ return btd_error_not_available(msg);
+
+ value = 0x01;
+ vmsg = g_strdup("Reset Control Point");
+ gatt_write_char(hr->attrib, hr->hrcp_val_handle, &value,
+ sizeof(value), char_write_cb, vmsg);
+
+ DBG("Energy Expended Value has been reset");
+
+ return dbus_message_new_method_return(msg);
+}
+
+static const GDBusMethodTable heartrate_device_methods[] = {
+ { GDBUS_METHOD("Reset", NULL, NULL,
+ hrcp_reset) },
+ { }
+};
+
int heartrate_adapter_register(struct btd_adapter *adapter)
{
struct heartrate_adapter *hradapter;
@@ -685,6 +715,18 @@ int heartrate_device_register(struct btd_device *device,
hr->dev = btd_device_ref(device);
hr->hradapter = hradapter;
+ if (!g_dbus_register_interface(get_dbus_connection(),
+ device_get_path(device),
+ HEART_RATE_INTERFACE,
+ heartrate_device_methods,
+ NULL, NULL, hr,
+ destroy_heartrate)) {
+ error("D-Bus failed to register %s interface",
+ HEART_RATE_INTERFACE);
+ destroy_heartrate(hr);
+ return -EIO;
+ }
+
hr->svc_range = g_new0(struct att_range, 1);
hr->svc_range->start = prim->range.start;
hr->svc_range->end = prim->range.end;
@@ -718,5 +760,6 @@ void heartrate_device_unregister(struct btd_device *device)
hradapter->devices = g_slist_remove(hradapter->devices, hr);
- destroy_heartrate(hr);
+ g_dbus_unregister_interface(get_dbus_connection(),
+ device_get_path(device), HEART_RATE_INTERFACE);
}
--
1.7.11.3
This patch adds support to discover known Heart Rate Service characteristics.
---
lib/uuid.h | 3 +++
profiles/heartrate/heartrate.c | 43 +++++++++++++++++++++++++++++++++++++++++-
profiles/heartrate/heartrate.h | 3 ++-
profiles/heartrate/manager.c | 19 ++++++++++++++++++-
4 files changed, 65 insertions(+), 3 deletions(-)
diff --git a/lib/uuid.h b/lib/uuid.h
index 3488e66..9da1b54 100644
--- a/lib/uuid.h
+++ b/lib/uuid.h
@@ -64,6 +64,9 @@ extern "C" {
#define SAP_UUID "0000112D-0000-1000-8000-00805f9b34fb"
#define HEART_RATE_UUID "0000180d-0000-1000-8000-00805f9b34fb"
+#define HEART_RATE_MEASUREMENT_UUID "00002a37-0000-1000-8000-00805f9b34fb"
+#define BODY_SENSOR_LOCATION_UUID "00002a38-0000-1000-8000-00805f9b34fb"
+#define HEART_RATE_CONTROL_POINT_UUID "00002a39-0000-1000-8000-00805f9b34fb"
#define HEALTH_THERMOMETER_UUID "00001809-0000-1000-8000-00805f9b34fb"
#define TEMPERATURE_MEASUREMENT_UUID "00002a1c-0000-1000-8000-00805f9b34fb"
diff --git a/profiles/heartrate/heartrate.c b/profiles/heartrate/heartrate.c
index e057180..a6733d3 100644
--- a/profiles/heartrate/heartrate.c
+++ b/profiles/heartrate/heartrate.c
@@ -48,6 +48,11 @@ struct heartrate {
struct heartrate_adapter *hradapter;
GAttrib *attrib;
guint attioid;
+
+ struct att_range *svc_range; /* primary svc range */
+
+ uint16_t measurement_val_handle;
+ uint16_t hrcp_val_handle;
};
static GSList *heartrate_adapters = NULL;
@@ -96,6 +101,7 @@ static void destroy_heartrate(gpointer user_data)
g_attrib_unref(hr->attrib);
btd_device_unref(hr->dev);
+ g_free(hr->svc_range);
g_free(hr);
}
@@ -106,6 +112,33 @@ static void destroy_heartrate_adapter(gpointer user_data)
g_free(hradapter);
}
+static void discover_char_cb(GSList *chars, guint8 status, gpointer user_data)
+{
+ struct heartrate *hr = user_data;
+
+ if (status) {
+ error("Discover HRS characteristics failed: %s",
+ att_ecode2str(status));
+ return;
+ }
+
+ for (; chars; chars = chars->next) {
+ struct gatt_char *c = chars->data;
+
+ if (g_strcmp0(c->uuid, HEART_RATE_MEASUREMENT_UUID) == 0) {
+ hr->measurement_val_handle = c->value_handle;
+ /* TODO: discover CCC handle */
+ } else if (g_strcmp0(c->uuid, BODY_SENSOR_LOCATION_UUID) == 0) {
+ DBG("Body Sensor Location supported");
+ /* TODO: read characterictic value */
+ } else if (g_strcmp0(c->uuid,
+ HEART_RATE_CONTROL_POINT_UUID) == 0) {
+ DBG("Heart Rate Control Point supported");
+ hr->hrcp_val_handle = c->value_handle;
+ }
+ }
+}
+
static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
{
struct heartrate *hr = user_data;
@@ -113,6 +146,9 @@ static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
DBG("");
hr->attrib = g_attrib_ref(attrib);
+
+ gatt_discover_char(hr->attrib, hr->svc_range->start, hr->svc_range->end,
+ NULL, discover_char_cb, hr);
}
static void attio_disconnected_cb(gpointer user_data)
@@ -150,7 +186,8 @@ void heartrate_adapter_unregister(struct btd_adapter *adapter)
destroy_heartrate_adapter(hradapter);
}
-int heartrate_device_register(struct btd_device *device)
+int heartrate_device_register(struct btd_device *device,
+ struct gatt_primary *prim)
{
struct btd_adapter *adapter;
struct heartrate_adapter *hradapter;
@@ -167,6 +204,10 @@ int heartrate_device_register(struct btd_device *device)
hr->dev = btd_device_ref(device);
hr->hradapter = hradapter;
+ hr->svc_range = g_new0(struct att_range, 1);
+ hr->svc_range->start = prim->range.start;
+ hr->svc_range->end = prim->range.end;
+
hradapter->devices = g_slist_prepend(hradapter->devices, hr);
hr->attioid = btd_device_add_attio_callback(device, attio_connected_cb,
diff --git a/profiles/heartrate/heartrate.h b/profiles/heartrate/heartrate.h
index 486f5b3..064939d 100644
--- a/profiles/heartrate/heartrate.h
+++ b/profiles/heartrate/heartrate.h
@@ -22,5 +22,6 @@
int heartrate_adapter_register(struct btd_adapter *adapter);
void heartrate_adapter_unregister(struct btd_adapter *adapter);
-int heartrate_device_register(struct btd_device *device);
+int heartrate_device_register(struct btd_device *device,
+ struct gatt_primary *prim);
void heartrate_device_unregister(struct btd_device *device);
diff --git a/profiles/heartrate/manager.c b/profiles/heartrate/manager.c
index 2a9326d..a0997bd 100644
--- a/profiles/heartrate/manager.c
+++ b/profiles/heartrate/manager.c
@@ -34,6 +34,14 @@
#include "heartrate.h"
#include "manager.h"
+static gint primary_uuid_cmp(gconstpointer a, gconstpointer b)
+{
+ const struct gatt_primary *prim = a;
+ const char *uuid = b;
+
+ return g_strcmp0(prim->uuid, uuid);
+}
+
static int heartrate_adapter_probe(struct btd_adapter *adapter)
{
return heartrate_adapter_register(adapter);
@@ -46,7 +54,16 @@ static void heartrate_adapter_remove(struct btd_adapter *adapter)
static int heartrate_device_probe(struct btd_device *device, GSList *uuids)
{
- return heartrate_device_register(device);
+ GSList *primaries;
+ GSList *l;
+
+ primaries = btd_device_get_primaries(device);
+
+ l = g_slist_find_custom(primaries, HEART_RATE_UUID, primary_uuid_cmp);
+ if (l == NULL)
+ return -EINVAL;
+
+ return heartrate_device_register(device, l->data);
}
static void heartrate_device_remove(struct btd_device *device)
--
1.7.11.3
This patch adds support to discover CCC descriptor for Heart Rate Measurement
characteristic.
---
profiles/heartrate/heartrate.c | 66 +++++++++++++++++++++++++++++++++++++++++-
1 file changed, 65 insertions(+), 1 deletion(-)
diff --git a/profiles/heartrate/heartrate.c b/profiles/heartrate/heartrate.c
index a6733d3..d3d6d43 100644
--- a/profiles/heartrate/heartrate.c
+++ b/profiles/heartrate/heartrate.c
@@ -52,6 +52,7 @@ struct heartrate {
struct att_range *svc_range; /* primary svc range */
uint16_t measurement_val_handle;
+ uint16_t measurement_ccc_handle;
uint16_t hrcp_val_handle;
};
@@ -112,6 +113,65 @@ static void destroy_heartrate_adapter(gpointer user_data)
g_free(hradapter);
}
+static void discover_ccc_cb(guint8 status, const guint8 *pdu,
+ guint16 len, gpointer user_data)
+{
+ struct heartrate *hr = user_data;
+ struct att_data_list *list;
+ uint8_t format;
+ int i;
+
+ if (status != 0) {
+ error("Discover Heart Rate Measurement descriptors failed: %s",
+ att_ecode2str(status));
+ return;
+ }
+
+ list = dec_find_info_resp(pdu, len, &format);
+ if (list == NULL)
+ return;
+
+ if (format != ATT_FIND_INFO_RESP_FMT_16BIT)
+ goto done;
+
+ for (i = 0; i < list->num; i++) {
+ uint8_t *value;
+ uint16_t handle, uuid;
+
+ value = list->data[i];
+ handle = att_get_u16(value);
+ uuid = att_get_u16(value + 2);
+
+ if (uuid == GATT_CLIENT_CHARAC_CFG_UUID) {
+ hr->measurement_ccc_handle = handle;
+ break;
+ }
+ }
+
+done:
+ att_data_list_free(list);
+}
+
+static void discover_measurement_ccc(struct heartrate *hr,
+ struct gatt_char *c, struct gatt_char *c_next)
+{
+ uint16_t start, end;
+
+ start = c->value_handle + 1;
+
+ if (c_next != NULL) {
+ if (start == c_next->handle)
+ return;
+ end = c_next->handle - 1;
+ } else if (c->value_handle != hr->svc_range->end) {
+ end = hr->svc_range->end;
+ } else {
+ return;
+ }
+
+ gatt_find_info(hr->attrib, start, end, discover_ccc_cb, hr);
+}
+
static void discover_char_cb(GSList *chars, guint8 status, gpointer user_data)
{
struct heartrate *hr = user_data;
@@ -126,8 +186,12 @@ static void discover_char_cb(GSList *chars, guint8 status, gpointer user_data)
struct gatt_char *c = chars->data;
if (g_strcmp0(c->uuid, HEART_RATE_MEASUREMENT_UUID) == 0) {
+ struct gatt_char *c_next =
+ (chars->next ? chars->next->data : NULL);
+
hr->measurement_val_handle = c->value_handle;
- /* TODO: discover CCC handle */
+
+ discover_measurement_ccc(hr, c, c_next);
} else if (g_strcmp0(c->uuid, BODY_SENSOR_LOCATION_UUID) == 0) {
DBG("Body Sensor Location supported");
/* TODO: read characterictic value */
--
1.7.11.3
This patch adds support to enable notifications for Heart Rate Measurement
characteristic value. Notifications are enabled automatically when at least
one watcher is registered and disabled otherwise.
---
profiles/heartrate/heartrate.c | 67 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 67 insertions(+)
diff --git a/profiles/heartrate/heartrate.c b/profiles/heartrate/heartrate.c
index 760b294..60ab2df 100644
--- a/profiles/heartrate/heartrate.c
+++ b/profiles/heartrate/heartrate.c
@@ -207,6 +207,17 @@ static void read_sensor_location_cb(guint8 status, const guint8 *pdu,
hr->location = value;
}
+static void char_write_cb(guint8 status, const guint8 *pdu, guint16 len,
+ gpointer user_data)
+{
+ char *msg = user_data;
+
+ if (status != 0)
+ error("%s failed", msg);
+
+ g_free(msg);
+}
+
static void discover_ccc_cb(guint8 status, const guint8 *pdu,
guint16 len, gpointer user_data)
{
@@ -238,6 +249,18 @@ static void discover_ccc_cb(guint8 status, const guint8 *pdu,
if (uuid == GATT_CLIENT_CHARAC_CFG_UUID) {
hr->measurement_ccc_handle = handle;
+
+ if (g_slist_length(hr->hradapter->watchers) > 0) {
+ uint8_t value[2];
+ char *msg;
+
+ att_put_u16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT,
+ value);
+ msg = g_strdup("Enable measurement");
+
+ gatt_write_char(hr->attrib, handle, value,
+ sizeof(value), char_write_cb, msg);
+ }
break;
}
}
@@ -299,6 +322,40 @@ static void discover_char_cb(GSList *chars, guint8 status, gpointer user_data)
}
}
+static void enable_measurement(gpointer data, gpointer user_data)
+{
+ struct heartrate *hr = data;
+ uint16_t handle = hr->measurement_ccc_handle;
+ uint8_t value[2];
+ char *msg;
+
+ if (hr->attrib == NULL || !handle)
+ return;
+
+ att_put_u16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, value);
+ msg = g_strdup("Enable measurement");
+
+ gatt_write_char(hr->attrib, handle, value, sizeof(value),
+ char_write_cb, msg);
+}
+
+static void disable_measurement(gpointer data, gpointer user_data)
+{
+ struct heartrate *hr = data;
+ uint16_t handle = hr->measurement_ccc_handle;
+ uint8_t value[2];
+ char *msg;
+
+ if (hr->attrib == NULL || !handle)
+ return;
+
+ att_put_u16(0x0000, value);
+ msg = g_strdup("Disable measurement");
+
+ gatt_write_char(hr->attrib, handle, value, sizeof(value),
+ char_write_cb, msg);
+}
+
static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
{
struct heartrate *hr = user_data;
@@ -330,6 +387,10 @@ static void watcher_exit_cb(DBusConnection *conn, void *user_data)
hradapter->watchers = g_slist_remove(hradapter->watchers, watcher);
g_dbus_remove_watch(conn, watcher->id);
+
+
+ if (g_slist_length(hradapter->watchers) == 0)
+ g_slist_foreach(hradapter->devices, disable_measurement, 0);
}
static DBusMessage *register_watcher(DBusConnection *conn, DBusMessage *msg,
@@ -355,6 +416,9 @@ static DBusMessage *register_watcher(DBusConnection *conn, DBusMessage *msg,
watcher->srv = g_strdup(sender);
watcher->path = g_strdup(path);
+ if (g_slist_length(hradapter->watchers) == 0)
+ g_slist_foreach(hradapter->devices, enable_measurement, 0);
+
hradapter->watchers = g_slist_prepend(hradapter->watchers, watcher);
DBG("heartrate watcher [%s] registered", path);
@@ -381,6 +445,9 @@ static DBusMessage *unregister_watcher(DBusConnection *conn, DBusMessage *msg,
hradapter->watchers = g_slist_remove(hradapter->watchers, watcher);
g_dbus_remove_watch(conn, watcher->id);
+ if (g_slist_length(hradapter->watchers) == 0)
+ g_slist_foreach(hradapter->devices, disable_measurement, 0);
+
DBG("heartrate watcher [%s] unregistered", path);
return dbus_message_new_method_return(msg);
--
1.7.11.3
This patch adds processing of received Heart Rate Measurement characteristic
notifications and sends processed data to registered watchers.
---
profiles/heartrate/heartrate.c | 167 ++++++++++++++++++++++++++++++++++++++++-
1 file changed, 166 insertions(+), 1 deletion(-)
diff --git a/profiles/heartrate/heartrate.c b/profiles/heartrate/heartrate.c
index 60ab2df..2e8e896 100644
--- a/profiles/heartrate/heartrate.c
+++ b/profiles/heartrate/heartrate.c
@@ -42,6 +42,13 @@
#include "heartrate.h"
#define HEART_RATE_MANAGER_INTERFACE "org.bluez.HeartRateManager"
+#define HEART_RATE_WATCHER_INTERFACE "org.bluez.HeartRateWatcher"
+
+#define HR_VALUE_FORMAT 0x01
+#define SENSOR_CONTACT_DETECTED 0x02
+#define SENSOR_CONTACT_SUPPORT 0x04
+#define ENERGY_EXP_STATUS 0x08
+#define RR_INTERVAL 0x10
struct heartrate_adapter {
struct btd_adapter *adapter;
@@ -54,6 +61,7 @@ struct heartrate {
struct heartrate_adapter *hradapter;
GAttrib *attrib;
guint attioid;
+ guint attionotid;
struct att_range *svc_range; /* primary svc range */
@@ -72,6 +80,17 @@ struct watcher {
char *path;
};
+struct measurement {
+ struct heartrate *hr;
+ uint16_t value;
+ gboolean has_energy;
+ uint16_t energy;
+ gboolean has_contact;
+ gboolean contact;
+ uint16_t num_interval;
+ uint16_t *interval;
+};
+
static GSList *heartrate_adapters = NULL;
static gint cmp_adapter(gconstpointer a, gconstpointer b)
@@ -155,8 +174,10 @@ static void destroy_heartrate(gpointer user_data)
if (hr->attioid > 0)
btd_device_remove_attio_callback(hr->dev, hr->attioid);
- if (hr->attrib != NULL)
+ if (hr->attrib != NULL) {
+ g_attrib_unregister(hr->attrib, hr->attionotid);
g_attrib_unref(hr->attrib);
+ }
btd_device_unref(hr->dev);
g_free(hr->svc_range);
@@ -356,6 +377,142 @@ static void disable_measurement(gpointer data, gpointer user_data)
char_write_cb, msg);
}
+static void update_watcher(gpointer data, gpointer user_data)
+{
+ struct watcher *w = data;
+ struct measurement *m = user_data;
+ struct heartrate *hr = m->hr;
+ const gchar *path = device_get_path(hr->dev);
+ DBusMessageIter iter;
+ DBusMessageIter dict;
+ DBusMessage *msg;
+
+ msg = dbus_message_new_method_call(w->srv, w->path,
+ HEART_RATE_WATCHER_INTERFACE, "MeasurementReceived");
+ if (msg == NULL)
+ return;
+
+ dbus_message_iter_init_append(msg, &iter);
+
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH , &path);
+
+ 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, "Value", DBUS_TYPE_UINT16, &m->value);
+
+ if (m->has_energy)
+ dict_append_entry(&dict, "Energy", DBUS_TYPE_UINT16,
+ &m->energy);
+
+ if (m->has_contact)
+ dict_append_entry(&dict, "Contact", DBUS_TYPE_BOOLEAN,
+ &m->contact);
+
+ if (m->num_interval > 0)
+ dict_append_array(&dict, "Interval", DBUS_TYPE_UINT16,
+ &m->interval, m->num_interval);
+
+ dbus_message_iter_close_container(&iter, &dict);
+
+ dbus_message_set_no_reply(msg, TRUE);
+ g_dbus_send_message(get_dbus_connection(), msg);
+}
+
+static void process_measurement(struct heartrate *hr, const uint8_t *pdu,
+ uint16_t len)
+{
+ struct measurement m;
+ uint8_t flags;
+
+ flags = *pdu;
+
+ pdu++;
+ len--;
+
+ memset(&m, 0, sizeof(m));
+
+ if (flags & HR_VALUE_FORMAT) {
+ if (len < 2) {
+ error("Heart Rate Measurement field missing");
+ return;
+ }
+
+ m.value = att_get_u16(pdu);
+ pdu += 2;
+ len -= 2;
+ } else {
+ if (len < 1) {
+ error("Heart Rate Measurement field missing");
+ return;
+ }
+
+ m.value = *pdu;
+ pdu++;
+ len--;
+ }
+
+ if (flags & ENERGY_EXP_STATUS) {
+ if (len < 2) {
+ error("Energy Expended field missing");
+ return;
+ }
+
+ m.has_energy = TRUE;
+ m.energy = att_get_u16(pdu);
+ pdu += 2;
+ len -= 2;
+ }
+
+ if (flags & RR_INTERVAL) {
+ int i;
+
+ if (len == 0 || (len % 2 != 0)) {
+ error("RR-Interval field malformed");
+ return;
+ }
+
+ m.num_interval = len / 2;
+ m.interval = g_new(uint16_t, m.num_interval);
+
+ for (i = 0; i < m.num_interval; pdu += 2, i++)
+ m.interval[i] = att_get_u16(pdu);
+ }
+
+ if (flags & SENSOR_CONTACT_SUPPORT) {
+ m.has_contact = TRUE;
+ m.contact = !!(flags & SENSOR_CONTACT_DETECTED);
+ }
+
+ /* Notify all registered watchers */
+ m.hr = hr;
+ g_slist_foreach(hr->hradapter->watchers, update_watcher, &m);
+
+ g_free(m.interval);
+}
+
+static void notify_handler(const uint8_t *pdu, uint16_t len, gpointer user_data)
+{
+ struct heartrate *hr = user_data;
+ uint16_t handle;
+
+ /* should be at least opcode (1b) + handle (2b) */
+ if (len < 3) {
+ error("Invalid PDU received");
+ return;
+ }
+
+ handle = att_get_u16(pdu + 1);
+ if (handle != hr->measurement_val_handle) {
+ error("Unexpected handle: 0x%04x", handle);
+ return;
+ }
+
+ process_measurement(hr, pdu + 3, len - 3);
+}
+
static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
{
struct heartrate *hr = user_data;
@@ -364,6 +521,9 @@ static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
hr->attrib = g_attrib_ref(attrib);
+ hr->attionotid = g_attrib_register(hr->attrib, ATT_OP_HANDLE_NOTIFY,
+ notify_handler, hr, NULL);
+
gatt_discover_char(hr->attrib, hr->svc_range->start, hr->svc_range->end,
NULL, discover_char_cb, hr);
}
@@ -374,6 +534,11 @@ static void attio_disconnected_cb(gpointer user_data)
DBG("");
+ if (hr->attionotid > 0) {
+ g_attrib_unregister(hr->attrib, hr->attionotid);
+ hr->attionotid = 0;
+ }
+
g_attrib_unref(hr->attrib);
hr->attrib = NULL;
}
--
1.7.11.3
From: Rafal Garbat <[email protected]>
---
Makefile.tools | 4 +-
test/test-heartrate | 103 ++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 105 insertions(+), 2 deletions(-)
create mode 100755 test/test-heartrate
diff --git a/Makefile.tools b/Makefile.tools
index d3b6f57..f59a7c4 100644
--- a/Makefile.tools
+++ b/Makefile.tools
@@ -209,7 +209,7 @@ EXTRA_DIST += test/sap_client.py test/hsplay test/hsmicro \
test/test-network test/simple-agent test/simple-service \
test/simple-endpoint test/test-audio test/test-input \
test/test-sap-server test/test-oob test/test-attrib \
- test/test-proximity test/test-thermometer test/test-health \
- test/test-health-sink test/service-record.dtd \
+ test/test-proximity test/test-thermometer test/test-heartrate \
+ test/test-health test/test-health-sink test/service-record.dtd \
test/service-did.xml test/service-spp.xml test/service-opp.xml \
test/service-ftp.xml test/simple-player test/test-nap
diff --git a/test/test-heartrate b/test/test-heartrate
new file mode 100755
index 0000000..316375d
--- /dev/null
+++ b/test/test-heartrate
@@ -0,0 +1,103 @@
+#!/usr/bin/python
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+'''
+Heart Rate Monitor test script
+'''
+
+import gobject
+
+import sys
+import dbus
+import dbus.service
+import dbus.mainloop.glib
+from optparse import OptionParser, make_option
+
+class Watcher(dbus.service.Object):
+ @dbus.service.method("org.bluez.HeartRateWatcher",
+ in_signature="oa{sv}", out_signature="")
+ def MeasurementReceived(self, device, measure):
+ print("Measurement received from %s" % device)
+ print("Value: ", measure["Value"])
+
+ if "Energy" in measure:
+ print("Energy: ", measure["Energy"])
+
+ if "Contact" in measure:
+ print("Contact: ", measure["Contact"])
+
+ if "Interval" in measure:
+ for i in measure["Interval"]:
+ print("Interval: ", i)
+
+if __name__ == "__main__":
+ dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+ bus = dbus.SystemBus()
+
+ manager = dbus.Interface(bus.get_object("org.bluez", "/"),
+ "org.bluez.Manager")
+
+ option_list = [
+ make_option("-i", "--adapter", action="store",
+ type="string", dest="adapter"),
+ make_option("-b", "--device", action="store",
+ type="string", dest="address"),
+ ]
+
+ parser = OptionParser(option_list=option_list)
+
+ (options, args) = parser.parse_args()
+
+ if not options.address:
+ print("Usage: %s [-i <adapter>] -b <bdaddr> [cmd]" % (sys.argv[0]))
+ print("Possible commands:")
+ print("\tReset")
+ sys.exit(1)
+
+ if options.adapter:
+ adapter_path = manager.FindAdapter(options.adapter)
+ else:
+ adapter_path = manager.DefaultAdapter()
+
+ adapter = dbus.Interface(bus.get_object("org.bluez", adapter_path),
+ "org.bluez.Adapter")
+
+ heartrateManager = dbus.Interface(bus.get_object("org.bluez",
+ adapter_path), "org.bluez.HeartRateManager")
+
+ path = "/test/watcher"
+ heartrateManager.RegisterWatcher(path)
+
+ device_path = adapter.FindDevice(options.address)
+
+ device = dbus.Interface(bus.get_object("org.bluez", device_path),
+ "org.bluez.Device")
+
+ heartrate = dbus.Interface(bus.get_object("org.bluez",
+ device_path), "org.bluez.HeartRate")
+
+ watcher = Watcher(bus, path)
+
+ properties = heartrate.GetProperties()
+
+ if "Location" in properties:
+ print("Sensor location: %s" % properties["Location"])
+ else:
+ print("Sensor location is not supported")
+
+ if len(args) > 0:
+ if args[0] == "Reset":
+ reset_sup = properties["ResetSupported"]
+ if reset_sup:
+ heartrate.Reset()
+ else:
+ print("Reset not supported")
+ sys.exit(1)
+ else:
+ print("unknown command")
+ sys.exit(1)
+
+ mainloop = gobject.MainLoop()
+ mainloop.run()
--
1.7.11.3
From: Rafal Garbat <[email protected]>
---
src/bluetooth.conf | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/bluetooth.conf b/src/bluetooth.conf
index 664dbd9..77a9371 100644
--- a/src/bluetooth.conf
+++ b/src/bluetooth.conf
@@ -16,6 +16,7 @@
<allow send_interface="org.bluez.MediaPlayer"/>
<allow send_interface="org.bluez.Watcher"/>
<allow send_interface="org.bluez.ThermometerWatcher"/>
+ <allow send_interface="org.bluez.HeartRateWatcher"/>
</policy>
<policy at_console="true">
--
1.7.11.3
From: Rafal Garbat <[email protected]>
This patch adds support for org.bluez.HeartRateManager interface on adapters
which allows to register and unregister per-adapter watcher.
---
profiles/heartrate/heartrate.c | 154 ++++++++++++++++++++++++++++++++++++++++-
1 file changed, 153 insertions(+), 1 deletion(-)
diff --git a/profiles/heartrate/heartrate.c b/profiles/heartrate/heartrate.c
index 8c12101..760b294 100644
--- a/profiles/heartrate/heartrate.c
+++ b/profiles/heartrate/heartrate.c
@@ -24,13 +24,16 @@
#include <config.h>
#endif
+#include <gdbus.h>
#include <errno.h>
#include <stdbool.h>
#include <glib.h>
#include <bluetooth/uuid.h>
#include "adapter.h"
+#include "dbus-common.h"
#include "device.h"
+#include "error.h"
#include "gattrib.h"
#include "att.h"
#include "gatt.h"
@@ -38,9 +41,12 @@
#include "log.h"
#include "heartrate.h"
+#define HEART_RATE_MANAGER_INTERFACE "org.bluez.HeartRateManager"
+
struct heartrate_adapter {
struct btd_adapter *adapter;
GSList *devices;
+ GSList *watchers;
};
struct heartrate {
@@ -59,6 +65,13 @@ struct heartrate {
uint8_t location;
};
+struct watcher {
+ struct heartrate_adapter *hradapter;
+ guint id;
+ char *srv;
+ char *path;
+};
+
static GSList *heartrate_adapters = NULL;
static gint cmp_adapter(gconstpointer a, gconstpointer b)
@@ -83,6 +96,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 struct heartrate_adapter *
find_heartrate_adapter(struct btd_adapter *adapter)
{
@@ -94,6 +120,34 @@ find_heartrate_adapter(struct btd_adapter *adapter)
return l->data;
}
+static void destroy_watcher(gpointer user_data)
+{
+ struct watcher *watcher = user_data;
+
+ g_free(watcher->path);
+ g_free(watcher->srv);
+ g_free(watcher);
+}
+
+static struct watcher *find_watcher(GSList *list, const char *sender,
+ const char *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(list, match, cmp_watcher);
+ destroy_watcher(match);
+
+ if (l != NULL)
+ return l->data;
+
+ return NULL;
+}
+
static void destroy_heartrate(gpointer user_data)
{
struct heartrate *hr = user_data;
@@ -109,10 +163,19 @@ static void destroy_heartrate(gpointer user_data)
g_free(hr);
}
+static void remove_watcher(gpointer user_data)
+{
+ struct watcher *watcher = user_data;
+
+ g_dbus_remove_watch(get_dbus_connection(), watcher->id);
+}
+
static void destroy_heartrate_adapter(gpointer user_data)
{
struct heartrate_adapter *hradapter = user_data;
+ g_slist_free_full(hradapter->watchers, remove_watcher);
+
g_free(hradapter);
}
@@ -258,6 +321,81 @@ static void attio_disconnected_cb(gpointer user_data)
hr->attrib = NULL;
}
+static void watcher_exit_cb(DBusConnection *conn, void *user_data)
+{
+ struct watcher *watcher = user_data;
+ struct heartrate_adapter *hradapter = watcher->hradapter;
+
+ DBG("heartrate watcher [%s] disconnected", watcher->path);
+
+ hradapter->watchers = g_slist_remove(hradapter->watchers, watcher);
+ g_dbus_remove_watch(conn, watcher->id);
+}
+
+static DBusMessage *register_watcher(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct heartrate_adapter *hradapter = data;
+ struct watcher *watcher;
+ const char *sender = dbus_message_get_sender(msg);
+ char *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(hradapter->watchers, sender, path);
+ if (watcher != NULL)
+ return btd_error_already_exists(msg);
+
+ watcher = g_new0(struct watcher, 1);
+ watcher->hradapter = hradapter;
+ watcher->id = g_dbus_add_disconnect_watch(conn, sender, watcher_exit_cb,
+ watcher, destroy_watcher);
+ watcher->srv = g_strdup(sender);
+ watcher->path = g_strdup(path);
+
+ hradapter->watchers = g_slist_prepend(hradapter->watchers, watcher);
+
+ DBG("heartrate watcher [%s] registered", path);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *unregister_watcher(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct heartrate_adapter *hradapter = data;
+ struct watcher *watcher;
+ const char *sender = dbus_message_get_sender(msg);
+ char *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(hradapter->watchers, sender, path);
+ if (watcher == NULL)
+ return btd_error_does_not_exist(msg);
+
+ hradapter->watchers = g_slist_remove(hradapter->watchers, watcher);
+ g_dbus_remove_watch(conn, watcher->id);
+
+ DBG("heartrate watcher [%s] unregistered", path);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static const GDBusMethodTable heartrate_manager_methods[] = {
+ { GDBUS_METHOD("RegisterWatcher",
+ GDBUS_ARGS({ "agent", "o" }), NULL,
+ register_watcher) },
+ { GDBUS_METHOD("UnregisterWatcher",
+ GDBUS_ARGS({ "agent", "o" }), NULL,
+ unregister_watcher) },
+ { }
+};
+
int heartrate_adapter_register(struct btd_adapter *adapter)
{
struct heartrate_adapter *hradapter;
@@ -267,6 +405,18 @@ int heartrate_adapter_register(struct btd_adapter *adapter)
heartrate_adapters = g_slist_prepend(heartrate_adapters, hradapter);
+ if (!g_dbus_register_interface(get_dbus_connection(),
+ adapter_get_path(adapter),
+ HEART_RATE_MANAGER_INTERFACE,
+ heartrate_manager_methods,
+ NULL, NULL, hradapter,
+ destroy_heartrate_adapter)) {
+ error("D-Bus failed to register %s interface",
+ HEART_RATE_MANAGER_INTERFACE);
+ destroy_heartrate_adapter(hradapter);
+ return -EIO;
+ }
+
return 0;
}
@@ -280,7 +430,9 @@ void heartrate_adapter_unregister(struct btd_adapter *adapter)
heartrate_adapters = g_slist_remove(heartrate_adapters, hradapter);
- destroy_heartrate_adapter(hradapter);
+ g_dbus_unregister_interface(get_dbus_connection(),
+ adapter_get_path(hradapter->adapter),
+ HEART_RATE_MANAGER_INTERFACE);
}
int heartrate_device_register(struct btd_device *device,
--
1.7.11.3
From: Rafal Garbat <[email protected]>
This patch adds support to read and store Body Sensor Location characteristic
value.
---
profiles/heartrate/heartrate.c | 35 ++++++++++++++++++++++++++++++++++-
1 file changed, 34 insertions(+), 1 deletion(-)
diff --git a/profiles/heartrate/heartrate.c b/profiles/heartrate/heartrate.c
index d3d6d43..8c12101 100644
--- a/profiles/heartrate/heartrate.c
+++ b/profiles/heartrate/heartrate.c
@@ -54,6 +54,9 @@ struct heartrate {
uint16_t measurement_val_handle;
uint16_t measurement_ccc_handle;
uint16_t hrcp_val_handle;
+
+ gboolean has_location;
+ uint8_t location;
};
static GSList *heartrate_adapters = NULL;
@@ -113,6 +116,34 @@ static void destroy_heartrate_adapter(gpointer user_data)
g_free(hradapter);
}
+static void read_sensor_location_cb(guint8 status, const guint8 *pdu,
+ guint16 len, gpointer user_data)
+{
+ struct heartrate *hr = user_data;
+ uint8_t value;
+ ssize_t vlen;
+
+ if (status != 0) {
+ error("Body Sensor Location read failed: %s",
+ att_ecode2str(status));
+ return;
+ }
+
+ vlen = dec_read_resp(pdu, len, &value, sizeof(value));
+ if (vlen < 0) {
+ error("Protocol error");
+ return;
+ }
+
+ if (vlen != 1) {
+ error("Invalid length for Body Sensor Location");
+ return;
+ }
+
+ hr->has_location = TRUE;
+ hr->location = value;
+}
+
static void discover_ccc_cb(guint8 status, const guint8 *pdu,
guint16 len, gpointer user_data)
{
@@ -194,7 +225,9 @@ static void discover_char_cb(GSList *chars, guint8 status, gpointer user_data)
discover_measurement_ccc(hr, c, c_next);
} else if (g_strcmp0(c->uuid, BODY_SENSOR_LOCATION_UUID) == 0) {
DBG("Body Sensor Location supported");
- /* TODO: read characterictic value */
+
+ gatt_read_char(hr->attrib, c->value_handle, 0,
+ read_sensor_location_cb, hr);
} else if (g_strcmp0(c->uuid,
HEART_RATE_CONTROL_POINT_UUID) == 0) {
DBG("Heart Rate Control Point supported");
--
1.7.11.3
---
profiles/heartrate/heartrate.c | 35 +++++++++++++++++++++++++++++++++++
1 file changed, 35 insertions(+)
diff --git a/profiles/heartrate/heartrate.c b/profiles/heartrate/heartrate.c
index 87bc309..e057180 100644
--- a/profiles/heartrate/heartrate.c
+++ b/profiles/heartrate/heartrate.c
@@ -31,6 +31,11 @@
#include "adapter.h"
#include "device.h"
+#include "gattrib.h"
+#include "att.h"
+#include "gatt.h"
+#include "attio.h"
+#include "log.h"
#include "heartrate.h"
struct heartrate_adapter {
@@ -41,6 +46,8 @@ struct heartrate_adapter {
struct heartrate {
struct btd_device *dev;
struct heartrate_adapter *hradapter;
+ GAttrib *attrib;
+ guint attioid;
};
static GSList *heartrate_adapters = NULL;
@@ -82,6 +89,12 @@ static void destroy_heartrate(gpointer user_data)
{
struct heartrate *hr = user_data;
+ if (hr->attioid > 0)
+ btd_device_remove_attio_callback(hr->dev, hr->attioid);
+
+ if (hr->attrib != NULL)
+ g_attrib_unref(hr->attrib);
+
btd_device_unref(hr->dev);
g_free(hr);
}
@@ -93,6 +106,25 @@ static void destroy_heartrate_adapter(gpointer user_data)
g_free(hradapter);
}
+static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
+{
+ struct heartrate *hr = user_data;
+
+ DBG("");
+
+ hr->attrib = g_attrib_ref(attrib);
+}
+
+static void attio_disconnected_cb(gpointer user_data)
+{
+ struct heartrate *hr = user_data;
+
+ DBG("");
+
+ g_attrib_unref(hr->attrib);
+ hr->attrib = NULL;
+}
+
int heartrate_adapter_register(struct btd_adapter *adapter)
{
struct heartrate_adapter *hradapter;
@@ -137,6 +169,9 @@ int heartrate_device_register(struct btd_device *device)
hradapter->devices = g_slist_prepend(hradapter->devices, hr);
+ hr->attioid = btd_device_add_attio_callback(device, attio_connected_cb,
+ attio_disconnected_cb, hr);
+
return 0;
}
--
1.7.11.3
From: Rafal Garbat <[email protected]>
This patch adds initial support for the Heart Rate Profile client.
Profile driver is registered to keep track of adapters and devices.
---
Makefile.am | 9 ++-
lib/uuid.h | 2 +
profiles/heartrate/heartrate.c | 165 +++++++++++++++++++++++++++++++++++++++++
profiles/heartrate/heartrate.h | 26 +++++++
profiles/heartrate/main.c | 52 +++++++++++++
profiles/heartrate/manager.c | 76 +++++++++++++++++++
profiles/heartrate/manager.h | 24 ++++++
7 files changed, 352 insertions(+), 2 deletions(-)
create mode 100644 profiles/heartrate/heartrate.c
create mode 100644 profiles/heartrate/heartrate.h
create mode 100644 profiles/heartrate/main.c
create mode 100644 profiles/heartrate/manager.c
create mode 100644 profiles/heartrate/manager.h
diff --git a/Makefile.am b/Makefile.am
index 372111a..808a81f 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -211,7 +211,7 @@ endif
if GATTMODULES
builtin_modules += thermometer alert time gatt_example proximity deviceinfo \
- gatt
+ gatt heartrate
builtin_sources += profiles/thermometer/main.c \
profiles/thermometer/manager.h \
profiles/thermometer/manager.c \
@@ -240,7 +240,12 @@ builtin_sources += profiles/thermometer/main.c \
profiles/deviceinfo/deviceinfo.c \
profiles/gatt/main.c profiles/gatt/manager.h \
profiles/gatt/manager.c profiles/gatt/gas.h \
- profiles/gatt/gas.c
+ profiles/gatt/gas.c \
+ profiles/heartrate/main.c \
+ profiles/heartrate/manager.c \
+ profiles/heartrate/manager.h \
+ profiles/heartrate/heartrate.c \
+ profiles/heartrate/heartrate.h
endif
builtin_modules += formfactor
diff --git a/lib/uuid.h b/lib/uuid.h
index aa6efdf..3488e66 100644
--- a/lib/uuid.h
+++ b/lib/uuid.h
@@ -63,6 +63,8 @@ extern "C" {
#define SAP_UUID "0000112D-0000-1000-8000-00805f9b34fb"
+#define HEART_RATE_UUID "0000180d-0000-1000-8000-00805f9b34fb"
+
#define HEALTH_THERMOMETER_UUID "00001809-0000-1000-8000-00805f9b34fb"
#define TEMPERATURE_MEASUREMENT_UUID "00002a1c-0000-1000-8000-00805f9b34fb"
#define TEMPERATURE_TYPE_UUID "00002a1d-0000-1000-8000-00805f9b34fb"
diff --git a/profiles/heartrate/heartrate.c b/profiles/heartrate/heartrate.c
new file mode 100644
index 0000000..87bc309
--- /dev/null
+++ b/profiles/heartrate/heartrate.c
@@ -0,0 +1,165 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2012 Tieto Poland
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdbool.h>
+#include <glib.h>
+#include <bluetooth/uuid.h>
+
+#include "adapter.h"
+#include "device.h"
+#include "heartrate.h"
+
+struct heartrate_adapter {
+ struct btd_adapter *adapter;
+ GSList *devices;
+};
+
+struct heartrate {
+ struct btd_device *dev;
+ struct heartrate_adapter *hradapter;
+};
+
+static GSList *heartrate_adapters = NULL;
+
+static gint cmp_adapter(gconstpointer a, gconstpointer b)
+{
+ const struct heartrate_adapter *hradapter = a;
+ const struct btd_adapter *adapter = b;
+
+ if (adapter == hradapter->adapter)
+ return 0;
+
+ return -1;
+}
+
+static gint cmp_device(gconstpointer a, gconstpointer b)
+{
+ const struct heartrate *hr = a;
+ const struct btd_device *dev = b;
+
+ if (dev == hr->dev)
+ return 0;
+
+ return -1;
+}
+
+static struct heartrate_adapter *
+find_heartrate_adapter(struct btd_adapter *adapter)
+{
+ GSList *l = g_slist_find_custom(heartrate_adapters, adapter,
+ cmp_adapter);
+ if (!l)
+ return NULL;
+
+ return l->data;
+}
+
+static void destroy_heartrate(gpointer user_data)
+{
+ struct heartrate *hr = user_data;
+
+ btd_device_unref(hr->dev);
+ g_free(hr);
+}
+
+static void destroy_heartrate_adapter(gpointer user_data)
+{
+ struct heartrate_adapter *hradapter = user_data;
+
+ g_free(hradapter);
+}
+
+int heartrate_adapter_register(struct btd_adapter *adapter)
+{
+ struct heartrate_adapter *hradapter;
+
+ hradapter = g_new0(struct heartrate_adapter, 1);
+ hradapter->adapter = adapter;
+
+ heartrate_adapters = g_slist_prepend(heartrate_adapters, hradapter);
+
+ return 0;
+}
+
+void heartrate_adapter_unregister(struct btd_adapter *adapter)
+{
+ struct heartrate_adapter *hradapter;
+
+ hradapter = find_heartrate_adapter(adapter);
+ if (hradapter == NULL)
+ return;
+
+ heartrate_adapters = g_slist_remove(heartrate_adapters, hradapter);
+
+ destroy_heartrate_adapter(hradapter);
+}
+
+int heartrate_device_register(struct btd_device *device)
+{
+ struct btd_adapter *adapter;
+ struct heartrate_adapter *hradapter;
+ struct heartrate *hr;
+
+ adapter = device_get_adapter(device);
+
+ hradapter = find_heartrate_adapter(adapter);
+
+ if (hradapter == NULL)
+ return -1;
+
+ hr = g_new0(struct heartrate, 1);
+ hr->dev = btd_device_ref(device);
+ hr->hradapter = hradapter;
+
+ hradapter->devices = g_slist_prepend(hradapter->devices, hr);
+
+ return 0;
+}
+
+void heartrate_device_unregister(struct btd_device *device)
+{
+ struct btd_adapter *adapter;
+ struct heartrate_adapter *hradapter;
+ struct heartrate *hr;
+ GSList *l;
+
+ adapter = device_get_adapter(device);
+
+ hradapter = find_heartrate_adapter(adapter);
+ if (hradapter == NULL)
+ return;
+
+ l = g_slist_find_custom(hradapter->devices, device, cmp_device);
+ if (l == NULL)
+ return;
+
+ hr = l->data;
+
+ hradapter->devices = g_slist_remove(hradapter->devices, hr);
+
+ destroy_heartrate(hr);
+}
diff --git a/profiles/heartrate/heartrate.h b/profiles/heartrate/heartrate.h
new file mode 100644
index 0000000..486f5b3
--- /dev/null
+++ b/profiles/heartrate/heartrate.h
@@ -0,0 +1,26 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2012 Tieto Poland
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+int heartrate_adapter_register(struct btd_adapter *adapter);
+void heartrate_adapter_unregister(struct btd_adapter *adapter);
+int heartrate_device_register(struct btd_device *device);
+void heartrate_device_unregister(struct btd_device *device);
diff --git a/profiles/heartrate/main.c b/profiles/heartrate/main.c
new file mode 100644
index 0000000..40f34bc
--- /dev/null
+++ b/profiles/heartrate/main.c
@@ -0,0 +1,52 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2012 Tieto Poland
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdint.h>
+#include <glib.h>
+#include <errno.h>
+
+#include "plugin.h"
+#include "manager.h"
+#include "hcid.h"
+#include "log.h"
+
+static int heartrate_init(void)
+{
+ if (!main_opts.gatt_enabled) {
+ DBG("GATT is disabled");
+ return -ENOTSUP;
+ }
+
+ return heartrate_manager_init();
+}
+
+static void heartrate_exit(void)
+{
+ heartrate_manager_exit();
+}
+
+BLUETOOTH_PLUGIN_DEFINE(heartrate, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
+ heartrate_init, heartrate_exit)
diff --git a/profiles/heartrate/manager.c b/profiles/heartrate/manager.c
new file mode 100644
index 0000000..2a9326d
--- /dev/null
+++ b/profiles/heartrate/manager.c
@@ -0,0 +1,76 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2012 Tieto Poland
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <gdbus.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <bluetooth/uuid.h>
+
+#include "adapter.h"
+#include "device.h"
+#include "profile.h"
+#include "att.h"
+#include "gattrib.h"
+#include "gatt.h"
+#include "heartrate.h"
+#include "manager.h"
+
+static int heartrate_adapter_probe(struct btd_adapter *adapter)
+{
+ return heartrate_adapter_register(adapter);
+}
+
+static void heartrate_adapter_remove(struct btd_adapter *adapter)
+{
+ heartrate_adapter_unregister(adapter);
+}
+
+static int heartrate_device_probe(struct btd_device *device, GSList *uuids)
+{
+ return heartrate_device_register(device);
+}
+
+static void heartrate_device_remove(struct btd_device *device)
+{
+ heartrate_device_unregister(device);
+}
+
+static struct btd_profile hrp_profile = {
+ .name = "Heart Rate GATT Driver",
+ .remote_uuids = BTD_UUIDS(HEART_RATE_UUID),
+
+ .device_probe = heartrate_device_probe,
+ .device_remove = heartrate_device_remove,
+
+ .adapter_probe = heartrate_adapter_probe,
+ .adapter_remove = heartrate_adapter_remove,
+};
+
+int heartrate_manager_init(void)
+{
+ return btd_profile_register(&hrp_profile);
+}
+
+void heartrate_manager_exit(void)
+{
+ btd_profile_unregister(&hrp_profile);
+}
diff --git a/profiles/heartrate/manager.h b/profiles/heartrate/manager.h
new file mode 100644
index 0000000..de799f6
--- /dev/null
+++ b/profiles/heartrate/manager.h
@@ -0,0 +1,24 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2012 Tieto Poland
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+int heartrate_manager_init(void);
+void heartrate_manager_exit(void);
--
1.7.11.3
From: Santiago Carot-Nemesio <[email protected]>
---
doc/heartrate-api.txt | 84 +++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 84 insertions(+)
create mode 100644 doc/heartrate-api.txt
diff --git a/doc/heartrate-api.txt b/doc/heartrate-api.txt
new file mode 100644
index 0000000..1fa9d0b
--- /dev/null
+++ b/doc/heartrate-api.txt
@@ -0,0 +1,84 @@
+Heart Rate API description
+****************************************
+
+Copyright (C) 2012 Santiago Carot-Nemesio <[email protected]>
+Copyright (C) 2012 Tieto Poland
+
+Heart Rate Manager hierarchy
+============================
+
+Service org.bluez
+Interface org.bluez.HeartRateManager
+Object path [variable prefix]/{hci0,hci1,...}
+
+Methods RegisterWatcher(object agent)
+
+ Registers a watcher to monitor heart rate measurements.
+
+ Possible Errors: org.bluez.Error.InvalidArguments
+
+ UnregisterWatcher(object agent)
+
+ Unregisters a watcher.
+
+Heart Rate Profile hierarchy
+============================
+
+Service org.bluez
+Interface org.bluez.HeartRate
+Object path [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX
+
+Methods dict GetProperties()
+
+ Returns all properties for the interface. See the
+ Properties section for the available properties.
+
+ Reset()
+
+ Restart the accumulation of energy expended from zero.
+
+ Possible Errors: org.bluez.Error.NotSupported
+
+Properties String Location (optional) [readonly]
+
+ Possible values: "Other", "Chest", "Wrist","Finger",
+ "Hand", "Earlobe", "Foot"
+
+ boolean ResetSupported [readonly]
+
+ True if energy expended is supported.
+
+Heart Rate Watcher hierarchy
+
+============================
+Service unique name
+Interface org.bluez.HeartRateWatcher
+Object path freely definable
+
+Methods void MeasurementReceived(object device, dict measurement)
+
+ This callback is called whenever a heart rate
+ measurement is received from the heart rate device.
+
+ Measurement:
+
+ uint16 Value:
+
+ Measurement value expressed in beats per
+ minute (bpm)
+
+ uint16 Energy (optional):
+
+ Accumulated energy expended in kilo Joules
+
+ boolean Contact (optional):
+
+ true if skin contact is detected by sensor,
+ false otherwise
+
+ array{uint16} Interval (optional):
+
+ RR-Interval values which represent the time
+ between two consecutive R waves in an ECG.
+ Values are ordered starting from oldest to
+ most recent.
--
1.7.11.3