2012-09-21 15:15:57

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [PATCH v7 00/13] Heart Rate Profile plugin

Hi,

Changes since v6:
- rebased to latest upstream master
- fixed issues pointed out by Johan
(except double const stuff as discussed on IRC)


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



2012-09-21 15:16:09

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [PATCH v7 12/13] heartrate: Add HeartRateWatcher interface to default policy

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


2012-09-21 15:16:10

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [PATCH v7 13/13] heartrate: Add test script

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


2012-09-21 15:16:05

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [PATCH v7 08/13] heartrate: Add support to enable notifications

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 b07a343..dda030b 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)
{
@@ -237,7 +248,20 @@ static void discover_ccc_cb(guint8 status, const guint8 *pdu,
uuid = att_get_u16(value + 2);

if (uuid == GATT_CLIENT_CHARAC_CFG_UUID) {
+ uint8_t value[2];
+ char *msg;
+
hr->measurement_ccc_handle = handle;
+
+ if (g_slist_length(hr->hradapter->watchers) == 0)
+ break;
+
+ 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 +323,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 +388,9 @@ 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


2012-09-21 15:16:08

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [PATCH v7 11/13] heartrate: Add GetProperties method

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 add34b8..81dd5cc 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


2012-09-21 15:16:06

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [PATCH v7 09/13] heartrate: Process measurement notifications

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 dda030b..ba1c1fa 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);
@@ -357,6 +378,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(btd_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;
@@ -365,6 +522,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);
}
@@ -375,6 +535,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


2012-09-21 15:16:07

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [PATCH v7 10/13] heartrate: Add support to reset Energy Expended

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 ba1c1fa..add34b8 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(btd_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(btd_get_dbus_connection(),
+ device_get_path(device), HEART_RATE_INTERFACE);
}
--
1.7.11.3


2012-09-21 15:16:03

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [PATCH v7 06/13] heartrate: Read Body Sensor Location characteristics

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..2547f9b 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 != sizeof(value)) {
+ 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


2012-09-21 15:16:02

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [PATCH v7 05/13] heartrate: Discover Heart Rate Measurement CCC

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


2012-09-21 15:16:04

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [PATCH v7 07/13] heartrate: Add HeartRateManager interface

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 2547f9b..b07a343 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(btd_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(btd_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(btd_get_dbus_connection(),
+ adapter_get_path(hradapter->adapter),
+ HEART_RATE_MANAGER_INTERFACE);
}

int heartrate_device_register(struct btd_device *device,
--
1.7.11.3


2012-09-21 15:16:00

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [PATCH v7 03/13] heartrate: Add attio callbacks

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


2012-09-21 15:16:01

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [PATCH v7 04/13] heartrate: Discover HRS characteristics

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


2012-09-21 15:15:59

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [PATCH v7 02/13] heartrate: Add initial HRP client support

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


2012-09-21 15:15:58

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [PATCH v7 01/13] Heart Rate Profile (HRP) client API

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