2012-09-06 11:32:34

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [PATCH v3 00/17] Heart Rate Profile plugin

Hi,

Changes since v2:
* rewritten MeasurementReceived description in API
* minor coding style fix
(both suggested by Vinicius, thanks)


Andrzej Kaczmarek (6):
heartrate: Add attio callbacks
heartrate: Enable measurement when watchers are registered
heartrate: Read Heart Rate Control Point characteristics
heartrate: Handle characteristics value changed notification
heartrate: Process Heart Rate Measurement characteristics
heartrate: Add Reset method

Rafal Garbat (10):
heartrate: Add Heart Rate Profile client
heartrate: Discover characteristics
heartrate: Process characteristics
heartrate: Discover characteristics descriptors
heartrate: Process characteristics descriptors
heartrate: Add HeartRateManager interface
heartrate: Read Body Sensor Location characteristics
heartrate: Add GetProperties method
heartrate: Add HeartRateWatcher interface to default policy
heartrate: Add test script

Santiago Carot-Nemesio (1):
Heart Rate Profile API

Makefile.am | 9 +-
Makefile.tools | 4 +-
doc/heartrate-api.txt | 84 ++++
lib/uuid.h | 5 +
profiles/heartrate/heartrate.c | 971 +++++++++++++++++++++++++++++++++++++++++
profiles/heartrate/heartrate.h | 27 ++
profiles/heartrate/main.c | 52 +++
profiles/heartrate/manager.c | 95 ++++
profiles/heartrate/manager.h | 24 +
src/bluetooth.conf | 1 +
test/test-heartrate | 103 +++++
11 files changed, 1371 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-06 11:32:46

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [PATCH v3 12/17] heartrate: Handle characteristics value changed notification

This patch adds stub for handling characteristics value changed notification.

---
profiles/heartrate/heartrate.c | 23 ++++++++++++++++++++++-
1 file changed, 22 insertions(+), 1 deletion(-)

diff --git a/profiles/heartrate/heartrate.c b/profiles/heartrate/heartrate.c
index 4591e56..f78d91d 100644
--- a/profiles/heartrate/heartrate.c
+++ b/profiles/heartrate/heartrate.c
@@ -43,6 +43,8 @@

#define HEART_RATE_MANAGER_IFACE "org.bluez.HeartRateManager"

+#define MIN_NOTIFICATION_LEN 3 /* 1-byte opcode + 2-byte handle */
+
struct heartrate_adapter {
struct btd_adapter *adapter;
GSList *devices;
@@ -54,6 +56,7 @@ struct heartrate {
struct heartrate_adapter *hra;
GAttrib *attrib;
guint attioid;
+ guint attionotid;
struct att_range *svc_range;
GSList *chars;
gboolean has_location;
@@ -176,8 +179,10 @@ static void destroy_heartrate_device(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);
+ }

if (hr->chars != NULL)
g_slist_free_full(hr->chars, destroy_char);
@@ -547,6 +552,14 @@ static void configure_heartrate_cb(GSList *characteristics, guint8 status,
}
}

+static void notify_handler(const uint8_t *pdu, uint16_t len, gpointer user_data)
+{
+ if (len < MIN_NOTIFICATION_LEN) {
+ error("Bad pdu received");
+ return;
+ }
+}
+
static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
{
struct heartrate *hr = user_data;
@@ -555,6 +568,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,
configure_heartrate_cb, hr);
@@ -566,6 +582,11 @@ static void attio_disconnected_cb(gpointer user_data)

DBG("GATT Disconnected");

+ 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-06 11:32:50

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [PATCH v3 16/17] 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-06 11:32:51

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [PATCH v3 17/17] 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-06 11:32:44

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [PATCH v3 10/17] heartrate: Read Body Sensor Location characteristics

From: Rafal Garbat <[email protected]>

This patch reads and stores Body Sensor Location characteristics value.

---
profiles/heartrate/heartrate.c | 40 +++++++++++++++++++++++++++++++++++++---
1 file changed, 37 insertions(+), 3 deletions(-)

diff --git a/profiles/heartrate/heartrate.c b/profiles/heartrate/heartrate.c
index 9d29ee0..150a675 100644
--- a/profiles/heartrate/heartrate.c
+++ b/profiles/heartrate/heartrate.c
@@ -56,6 +56,8 @@ struct heartrate {
guint attioid;
struct att_range *svc_range;
GSList *chars;
+ gboolean has_location;
+ uint8_t location;
};

struct characteristic {
@@ -385,12 +387,44 @@ static const GDBusMethodTable heartrate_manager_methods[] = {
{ }
};

+static void read_sensor_location_cb(guint8 status, const guint8 *pdu,
+ guint16 len, gpointer user_data)
+{
+ struct characteristic *ch = user_data;
+ struct heartrate *hr = ch->hr;
+ uint8_t value;
+ ssize_t vlen;
+
+ if (status != 0) {
+ error("Body Sensor Location value 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 process_heartrate_char(struct characteristic *ch)
{
- if (g_strcmp0(ch->attr.uuid, HEART_RATE_CONTROL_POINT_UUID) == 0)
- DBG("Heart Rate Control Point supported by client");
- else if (g_strcmp0(ch->attr.uuid, BODY_SENSOR_LOCATION_UUID) == 0)
+ if (g_strcmp0(ch->attr.uuid, HEART_RATE_CONTROL_POINT_UUID) == 0) {
+ DBG("Heart Rate Control Point reset supported by client");
+ } else if (g_strcmp0(ch->attr.uuid, BODY_SENSOR_LOCATION_UUID) == 0) {
DBG("Body Sensor Location supported by client");
+ gatt_read_char(ch->hr->attrib, ch->attr.value_handle, 0,
+ read_sensor_location_cb, ch);
+ }
}

static void process_heartrate_desc(struct descriptor *desc)
--
1.7.11.3


2012-09-06 11:32:45

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [PATCH v3 11/17] heartrate: Read Heart Rate Control Point characteristics

This patch reads and stores Heart Rate Control Point characteristics value.

---
profiles/heartrate/heartrate.c | 2 ++
1 file changed, 2 insertions(+)

diff --git a/profiles/heartrate/heartrate.c b/profiles/heartrate/heartrate.c
index 150a675..4591e56 100644
--- a/profiles/heartrate/heartrate.c
+++ b/profiles/heartrate/heartrate.c
@@ -58,6 +58,7 @@ struct heartrate {
GSList *chars;
gboolean has_location;
uint8_t location;
+ gboolean has_cp_reset;
};

struct characteristic {
@@ -420,6 +421,7 @@ static void process_heartrate_char(struct characteristic *ch)
{
if (g_strcmp0(ch->attr.uuid, HEART_RATE_CONTROL_POINT_UUID) == 0) {
DBG("Heart Rate Control Point reset supported by client");
+ ch->hr->has_cp_reset = TRUE;
} else if (g_strcmp0(ch->attr.uuid, BODY_SENSOR_LOCATION_UUID) == 0) {
DBG("Body Sensor Location supported by client");
gatt_read_char(ch->hr->attrib, ch->attr.value_handle, 0,
--
1.7.11.3


2012-09-06 11:32:49

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [PATCH v3 15/17] heartrate: Add GetProperties method

From: Rafal Garbat <[email protected]>

---
profiles/heartrate/heartrate.c | 57 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 57 insertions(+)

diff --git a/profiles/heartrate/heartrate.c b/profiles/heartrate/heartrate.c
index 48d3cd1..43f94c1 100644
--- a/profiles/heartrate/heartrate.c
+++ b/profiles/heartrate/heartrate.c
@@ -104,6 +104,25 @@ struct watcher {

static GSList *heartrate_adapters = NULL;

+static const char * const location_type[] = {
+ "Other",
+ "Chest",
+ "Wrist",
+ "Finger",
+ "Hand",
+ "Earlobe",
+ "Foot"
+};
+
+static const gchar *location2str(uint8_t value)
+{
+ if (value < G_N_ELEMENTS(location_type))
+ return location_type[value];
+
+ error("Location type %d reserved for future use", value);
+ return NULL;
+}
+
static gint cmp_adapter(gconstpointer a, gconstpointer b)
{
const struct heartrate_adapter *hra = a;
@@ -410,6 +429,41 @@ static DBusMessage *unregister_watcher(DBusConnection *conn, DBusMessage *msg,
return dbus_message_new_method_return(msg);
}

+static DBusMessage *get_properties(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct heartrate *hr = data;
+ DBusMessageIter iter;
+ DBusMessageIter dict;
+ DBusMessage *reply;
+
+ reply = dbus_message_new_method_return(msg);
+ if (reply == NULL)
+ return NULL;
+
+ dbus_message_iter_init_append(reply, &iter);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+ if (hr->has_location) {
+ char *loc = g_strdup(location2str(hr->location));
+
+ dict_append_entry(&dict, "Location", DBUS_TYPE_STRING, &loc);
+
+ g_free(loc);
+ }
+
+ dict_append_entry(&dict, "ResetSupported", DBUS_TYPE_BOOLEAN,
+ &hr->has_cp_reset);
+
+ dbus_message_iter_close_container(&iter, &dict);
+
+ return reply;
+}
+
static gint process_att_reset(struct heartrate *hr)
{
struct characteristic *ch;
@@ -458,6 +512,9 @@ static const GDBusMethodTable heartrate_manager_methods[] = {
};

static const GDBusMethodTable heartrate_device_methods[] = {
+ { GDBUS_METHOD("GetProperties",
+ NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
+ get_properties) },
{ GDBUS_METHOD("Reset", NULL, NULL,
control_point_reset) },
{ }
--
1.7.11.3


2012-09-06 11:32:47

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [PATCH v3 13/17] heartrate: Process Heart Rate Measurement characteristics

---
profiles/heartrate/heartrate.c | 171 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 171 insertions(+)

diff --git a/profiles/heartrate/heartrate.c b/profiles/heartrate/heartrate.c
index f78d91d..6549d4b 100644
--- a/profiles/heartrate/heartrate.c
+++ b/profiles/heartrate/heartrate.c
@@ -42,9 +42,16 @@
#include "log.h"

#define HEART_RATE_MANAGER_IFACE "org.bluez.HeartRateManager"
+#define HEART_RATE_WATCHER_IFACE "org.bluez.HeartRateWatcher"

#define MIN_NOTIFICATION_LEN 3 /* 1-byte opcode + 2-byte handle */

+#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;
GSList *devices;
@@ -76,6 +83,17 @@ struct descriptor {
bt_uuid_t uuid;
};

+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;
+};
+
struct watcher {
struct heartrate_adapter *hra;
guint id;
@@ -123,6 +141,14 @@ static gint cmp_descriptor(gconstpointer a, gconstpointer b)
return bt_uuid_cmp(&desc->uuid, uuid);
}

+static gint cmp_char_val_handle(gconstpointer a, gconstpointer b)
+{
+ const struct characteristic *ch = a;
+ const uint16_t *handle = b;
+
+ return ch->attr.value_handle - *handle;
+}
+
static gint cmp_watcher(gconstpointer a, gconstpointer b)
{
const struct watcher *watcher = a;
@@ -552,12 +578,157 @@ static void configure_heartrate_cb(GSList *characteristics, guint8 status,
}
}

+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_IFACE,
+ "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 recv_measurement(struct heartrate *hr, struct measurement *m)
+{
+ GSList *wlist = hr->hra->watchers;
+
+ m->hr = hr;
+
+ g_slist_foreach(wlist, update_watcher, m);
+}
+
+static void proc_measurement(struct heartrate *hr, const uint8_t *pdu,
+ uint16_t len)
+{
+ struct measurement m;
+ uint8_t flags;
+
+ if (len < 4) {
+ error("Mandatory flags are not provided");
+ return;
+ }
+
+ flags = pdu[3];
+
+ pdu += 4;
+ len -= 4;
+
+ 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);
+ }
+
+ recv_measurement(hr, &m);
+
+ g_free(m.interval);
+}
+
static void notify_handler(const uint8_t *pdu, uint16_t len, gpointer user_data)
{
+ struct heartrate *hr = user_data;
+ const struct characteristic *ch;
+ uint16_t handle;
+ GSList *l;
+
if (len < MIN_NOTIFICATION_LEN) {
error("Bad pdu received");
return;
}
+
+ handle = att_get_u16(&pdu[1]);
+ l = g_slist_find_custom(hr->chars, &handle, cmp_char_val_handle);
+ if (l == NULL) {
+ error("Unexpected handle: 0x%04x", handle);
+ return;
+ }
+
+ ch = l->data;
+ if (g_strcmp0(ch->attr.uuid, HEART_RATE_MEASUREMENT_UUID) == 0)
+ proc_measurement(hr, pdu, len);
}

static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
--
1.7.11.3


2012-09-06 11:32:48

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [PATCH v3 14/17] heartrate: Add Reset method

This patch adds Reset method on HeartRate interface to reset Energy Expended.

---
profiles/heartrate/heartrate.c | 58 +++++++++++++++++++++++++++++++++++++++++-
1 file changed, 57 insertions(+), 1 deletion(-)

diff --git a/profiles/heartrate/heartrate.c b/profiles/heartrate/heartrate.c
index 6549d4b..48d3cd1 100644
--- a/profiles/heartrate/heartrate.c
+++ b/profiles/heartrate/heartrate.c
@@ -43,6 +43,7 @@

#define HEART_RATE_MANAGER_IFACE "org.bluez.HeartRateManager"
#define HEART_RATE_WATCHER_IFACE "org.bluez.HeartRateWatcher"
+#define HEART_RATE_IFACE "org.bluez.HeartRate"

#define MIN_NOTIFICATION_LEN 3 /* 1-byte opcode + 2-byte handle */

@@ -409,6 +410,43 @@ static DBusMessage *unregister_watcher(DBusConnection *conn, DBusMessage *msg,
return dbus_message_new_method_return(msg);
}

+static gint process_att_reset(struct heartrate *hr)
+{
+ struct characteristic *ch;
+ char *msg;
+ uint8_t atval;
+
+ if (hr->attrib == NULL)
+ return -1;
+
+ ch = get_characteristic(hr, HEART_RATE_CONTROL_POINT_UUID);
+ if (ch == NULL)
+ return -1;
+
+ atval = 0x01;
+ msg = g_strdup("Reset Control Point");
+
+ gatt_write_char(hr->attrib, ch->attr.value_handle, &atval,
+ 1, char_write_cb, msg);
+ return 0;
+}
+
+static DBusMessage *control_point_reset(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct heartrate *hr = data;
+
+ if (!hr->has_cp_reset)
+ return btd_error_not_supported(msg);
+
+ if (process_att_reset(hr) < 0)
+ return btd_error_not_available(msg);
+
+ DBG("Energy Expended Value has been reset");
+
+ return dbus_message_new_method_return(msg);
+}
+
static const GDBusMethodTable heartrate_manager_methods[] = {
{ GDBUS_METHOD("RegisterWatcher",
GDBUS_ARGS({ "agent", "o" }), NULL,
@@ -419,6 +457,12 @@ static const GDBusMethodTable heartrate_manager_methods[] = {
{ }
};

+static const GDBusMethodTable heartrate_device_methods[] = {
+ { GDBUS_METHOD("Reset", NULL, NULL,
+ control_point_reset) },
+ { }
+};
+
static void read_sensor_location_cb(guint8 status, const guint8 *pdu,
guint16 len, gpointer user_data)
{
@@ -819,6 +863,16 @@ int heartrate_device_register(struct btd_device *device,
hr->dev = btd_device_ref(device);
hr->hra = hra;

+ if (!g_dbus_register_interface(get_dbus_connection(),
+ device_get_path(device), HEART_RATE_IFACE,
+ heartrate_device_methods, NULL, NULL,
+ hr, destroy_heartrate_device)) {
+ error("D-Bus failed to register %s interface",
+ HEART_RATE_IFACE);
+ destroy_heartrate_device(hr);
+ return -EIO;
+ }
+
hra->devices = g_slist_prepend(hra->devices, hr);

hr->svc_range = g_new0(struct att_range, 1);
@@ -854,5 +908,7 @@ void heartrate_device_unregister(struct btd_device *device)

hra->devices = g_slist_remove(hra->devices, hr);

- destroy_heartrate_device(hr);
+ g_dbus_unregister_interface(get_dbus_connection(),
+ device_get_path(device),
+ HEART_RATE_IFACE);
}
--
1.7.11.3


2012-09-06 11:32:43

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [PATCH v3 09/17] heartrate: Enable measurement when watchers are registered

This patch will enable measurement notification when first watcher is
registered or when device is connected and watcher is already registered.

Measurement will be disabled when last watcher is unregistered.

---
profiles/heartrate/heartrate.c | 129 ++++++++++++++++++++++++++++++++++++++++-
1 file changed, 128 insertions(+), 1 deletion(-)

diff --git a/profiles/heartrate/heartrate.c b/profiles/heartrate/heartrate.c
index a223fe0..9d29ee0 100644
--- a/profiles/heartrate/heartrate.c
+++ b/profiles/heartrate/heartrate.c
@@ -101,6 +101,22 @@ static gint cmp_device(gconstpointer a, gconstpointer b)
return -1;
}

+static gint cmp_char_uuid(gconstpointer a, gconstpointer b)
+{
+ const struct characteristic *ch = a;
+ const char *uuid = b;
+
+ return g_strcmp0(ch->attr.uuid, uuid);
+}
+
+static gint cmp_descriptor(gconstpointer a, gconstpointer b)
+{
+ const struct descriptor *desc = a;
+ const bt_uuid_t *uuid = b;
+
+ return bt_uuid_cmp(&desc->uuid, uuid);
+}
+
static gint cmp_watcher(gconstpointer a, gconstpointer b)
{
const struct watcher *watcher = a;
@@ -181,6 +197,91 @@ static void destroy_heartrate_adapter(gpointer user_data)
g_free(hra);
}

+static struct characteristic *get_characteristic(struct heartrate *hr,
+ const char *uuid)
+{
+ GSList *l;
+
+ l = g_slist_find_custom(hr->chars, uuid, cmp_char_uuid);
+ if (l == NULL)
+ return NULL;
+
+ return l->data;
+}
+
+static struct descriptor *get_descriptor(struct characteristic *ch,
+ const bt_uuid_t *uuid)
+{
+ GSList *l;
+
+ l = g_slist_find_custom(ch->desc, uuid, cmp_descriptor);
+ if (l == NULL)
+ return NULL;
+
+ return l->data;
+}
+
+static void 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 measurement_toggle(struct heartrate *hr, gboolean enable)
+{
+ struct characteristic *ch;
+ struct descriptor *desc;
+ bt_uuid_t btuuid;
+ uint8_t atval[2];
+ char *msg;
+
+ if (hr->attrib == NULL)
+ return;
+
+ ch = get_characteristic(hr, HEART_RATE_MEASUREMENT_UUID);
+ if (ch == NULL) {
+ DBG("Heart Rate Measurement characteristic not found");
+ return;
+ }
+
+ bt_uuid16_create(&btuuid, GATT_CLIENT_CHARAC_CFG_UUID);
+ desc = get_descriptor(ch, &btuuid);
+ if (desc == NULL) {
+ DBG("Client Characteristic Configuration descriptor not found");
+ return;
+ }
+
+ if (enable) {
+ att_put_u16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, atval);
+ msg = g_strdup("Enable measurement");
+ } else {
+ att_put_u16(0x0000, atval);
+ msg = g_strdup("Disable measurement");
+ }
+
+ gatt_write_char(hr->attrib, desc->handle, atval, 2, char_write_cb, msg);
+
+}
+
+static void measurement_enable(gpointer data, gpointer user_data)
+{
+ struct heartrate *hr = data;
+
+ measurement_toggle(hr, TRUE);
+}
+
+static void measurement_disable(gpointer data, gpointer user_data)
+{
+ struct heartrate *hr = data;
+
+ measurement_toggle(hr, FALSE);
+}
+
static void watcher_exit(DBusConnection *conn, void *user_data)
{
struct watcher *watcher = user_data;
@@ -190,6 +291,9 @@ static void watcher_exit(DBusConnection *conn, void *user_data)

hr->watchers = g_slist_remove(hr->watchers, watcher);
g_dbus_remove_watch(conn, watcher->id);
+
+ if (g_slist_length(hr->watchers) == 0)
+ g_slist_foreach(hr->devices, measurement_disable, 0);
}

static struct watcher *find_watcher(GSList *list, const char *sender,
@@ -236,6 +340,9 @@ static DBusMessage *register_watcher(DBusConnection *conn, DBusMessage *msg,
watcher->id = g_dbus_add_disconnect_watch(conn, sender, watcher_exit,
watcher, destroy_watcher);

+ if (g_slist_length(hra->watchers) == 0)
+ g_slist_foreach(hra->devices, measurement_enable, 0);
+
hra->watchers = g_slist_prepend(hra->watchers, watcher);

return dbus_message_new_method_return(msg);
@@ -262,6 +369,9 @@ static DBusMessage *unregister_watcher(DBusConnection *conn, DBusMessage *msg,
hr->watchers = g_slist_remove(hr->watchers, watcher);
g_dbus_remove_watch(get_dbus_connection(), watcher->id);

+ if (g_slist_length(hr->watchers) == 0)
+ g_slist_foreach(hr->devices, measurement_disable, 0);
+
return dbus_message_new_method_return(msg);
}

@@ -291,9 +401,26 @@ static void process_heartrate_desc(struct descriptor *desc)

bt_uuid16_create(&btuuid, GATT_CLIENT_CHARAC_CFG_UUID);

- if (bt_uuid_cmp(&desc->uuid, &btuuid) == 0)
+ if (bt_uuid_cmp(&desc->uuid, &btuuid) == 0) {
+ uint8_t atval[2];
+ char *msg;
+
+ if (g_strcmp0(ch->attr.uuid, HEART_RATE_MEASUREMENT_UUID) != 0)
+ goto done;
+
+ if (g_slist_length(ch->hr->hra->watchers) == 0)
+ return;
+
+ att_put_u16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, atval);
+ msg = g_strdup("Enable measurement");
+
+ gatt_write_char(ch->hr->attrib, desc->handle,
+ atval, 2, char_write_cb, msg);
+
return;
+ }

+done:
bt_uuid_to_string(&desc->uuid, uuidstr, MAX_LEN_UUID_STR);
DBG("Ignored descriptor %s in characteristic %s", uuidstr,
ch->attr.uuid);
--
1.7.11.3


2012-09-06 11:32:42

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [PATCH v3 08/17] 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 | 155 ++++++++++++++++++++++++++++++++++++++++-
1 file changed, 154 insertions(+), 1 deletion(-)

diff --git a/profiles/heartrate/heartrate.c b/profiles/heartrate/heartrate.c
index eec8ae0..a223fe0 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 "attio.h"
#include "att.h"
@@ -38,9 +41,12 @@
#include "heartrate.h"
#include "log.h"

+#define HEART_RATE_MANAGER_IFACE "org.bluez.HeartRateManager"
+
struct heartrate_adapter {
struct btd_adapter *adapter;
GSList *devices;
+ GSList *watchers;
};

struct heartrate {
@@ -64,6 +70,13 @@ struct descriptor {
bt_uuid_t uuid;
};

+struct watcher {
+ struct heartrate_adapter *hra;
+ guint id;
+ char *srv;
+ char *path;
+};
+
static GSList *heartrate_adapters = NULL;

static gint cmp_adapter(gconstpointer a, gconstpointer b)
@@ -88,6 +101,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)
{
@@ -108,6 +134,22 @@ static void destroy_char(gpointer user_data)
g_free(c);
}

+static void remove_watcher(gpointer user_data)
+{
+ struct watcher *watcher = user_data;
+
+ g_dbus_remove_watch(get_dbus_connection(), watcher->id);
+}
+
+static void destroy_watcher(gpointer user_data)
+{
+ struct watcher *watcher = user_data;
+
+ g_free(watcher->path);
+ g_free(watcher->srv);
+ g_free(watcher);
+}
+
static void destroy_heartrate_device(gpointer user_data)
{
struct heartrate *hr = user_data;
@@ -133,9 +175,106 @@ static void destroy_heartrate_adapter(gpointer user_data)
if (hra->devices != NULL)
g_slist_free_full(hra->devices, destroy_heartrate_device);

+ if (hra->watchers != NULL)
+ g_slist_free_full(hra->watchers, remove_watcher);
+
g_free(hra);
}

+static void watcher_exit(DBusConnection *conn, void *user_data)
+{
+ struct watcher *watcher = user_data;
+ struct heartrate_adapter *hr = watcher->hra;
+
+ DBG("Heart Rate watcher %s disconnected", watcher->path);
+
+ hr->watchers = g_slist_remove(hr->watchers, watcher);
+ g_dbus_remove_watch(conn, watcher->id);
+}
+
+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 DBusMessage *register_watcher(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ const char *sender = dbus_message_get_sender(msg);
+ struct heartrate_adapter *hra = data;
+ struct watcher *watcher;
+ 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(hra->watchers, sender, path);
+ if (watcher != NULL)
+ return btd_error_already_exists(msg);
+
+ DBG("Heart Rate watcher %s registered", path);
+
+ watcher = g_new0(struct watcher, 1);
+ watcher->srv = g_strdup(sender);
+ watcher->path = g_strdup(path);
+ watcher->hra = hra;
+ watcher->id = g_dbus_add_disconnect_watch(conn, sender, watcher_exit,
+ watcher, destroy_watcher);
+
+ hra->watchers = g_slist_prepend(hra->watchers, watcher);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *unregister_watcher(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ const char *sender = dbus_message_get_sender(msg);
+ struct heartrate_adapter *hr = data;
+ struct watcher *watcher;
+ 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(hr->watchers, sender, path);
+ if (watcher == NULL)
+ return btd_error_does_not_exist(msg);
+
+ DBG("Heart Rate watcher %s unregistered", path);
+
+ hr->watchers = g_slist_remove(hr->watchers, watcher);
+ g_dbus_remove_watch(get_dbus_connection(), watcher->id);
+
+ 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) },
+ { }
+};
+
static void process_heartrate_char(struct characteristic *ch)
{
if (g_strcmp0(ch->attr.uuid, HEART_RATE_CONTROL_POINT_UUID) == 0)
@@ -275,6 +414,18 @@ int heartrate_adapter_register(struct btd_adapter *adapter)
hra = g_new0(struct heartrate_adapter, 1);
hra->adapter = adapter;

+ if (!g_dbus_register_interface(get_dbus_connection(),
+ adapter_get_path(adapter),
+ HEART_RATE_MANAGER_IFACE,
+ heartrate_manager_methods,
+ NULL, NULL, hra,
+ destroy_heartrate_adapter)) {
+ error("D-Bus failed to register %s interface",
+ HEART_RATE_MANAGER_IFACE);
+ destroy_heartrate_adapter(hra);
+ return -EIO;
+ }
+
heartrate_adapters = g_slist_prepend(heartrate_adapters, hra);

return 0;
@@ -290,7 +441,9 @@ void heartrate_adapter_unregister(struct btd_adapter *adapter)

heartrate_adapters = g_slist_remove(heartrate_adapters, hra);

- destroy_heartrate_adapter(hra);
+ g_dbus_unregister_interface(get_dbus_connection(),
+ adapter_get_path(hra->adapter),
+ HEART_RATE_MANAGER_IFACE);
}

int heartrate_device_register(struct btd_device *device,
--
1.7.11.3


2012-09-06 11:32:41

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [PATCH v3 07/17] heartrate: Process characteristics descriptors

From: Rafal Garbat <[email protected]>

This patch adds stub to process discovered characteristics descriptors.

---
lib/uuid.h | 1 +
profiles/heartrate/heartrate.c | 18 ++++++++++++++++++
2 files changed, 19 insertions(+)

diff --git a/lib/uuid.h b/lib/uuid.h
index 0a9db51..15a80a0 100644
--- a/lib/uuid.h
+++ b/lib/uuid.h
@@ -64,6 +64,7 @@ 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 HEART_RATE_CONTROL_POINT_UUID "00002a39-0000-1000-8000-00805f9b34fb"
#define BODY_SENSOR_LOCATION_UUID "00002a38-0000-1000-8000-00805f9b34fb"

diff --git a/profiles/heartrate/heartrate.c b/profiles/heartrate/heartrate.c
index 69d6d51..eec8ae0 100644
--- a/profiles/heartrate/heartrate.c
+++ b/profiles/heartrate/heartrate.c
@@ -144,6 +144,22 @@ static void process_heartrate_char(struct characteristic *ch)
DBG("Body Sensor Location supported by client");
}

+static void process_heartrate_desc(struct descriptor *desc)
+{
+ struct characteristic *ch = desc->ch;
+ char uuidstr[MAX_LEN_UUID_STR];
+ bt_uuid_t btuuid;
+
+ bt_uuid16_create(&btuuid, GATT_CLIENT_CHARAC_CFG_UUID);
+
+ if (bt_uuid_cmp(&desc->uuid, &btuuid) == 0)
+ return;
+
+ bt_uuid_to_string(&desc->uuid, uuidstr, MAX_LEN_UUID_STR);
+ DBG("Ignored descriptor %s in characteristic %s", uuidstr,
+ ch->attr.uuid);
+}
+
static void discover_desc_cb(guint8 status, const guint8 *pdu,
guint16 len, gpointer user_data)
{
@@ -177,6 +193,8 @@ static void discover_desc_cb(guint8 status, const guint8 *pdu,
desc->uuid = att_get_uuid128(value + 2);

ch->desc = g_slist_append(ch->desc, desc);
+
+ process_heartrate_desc(desc);
}

att_data_list_free(list);
--
1.7.11.3


2012-09-06 11:32:38

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [PATCH v3 04/17] heartrate: Discover characteristics

From: Rafal Garbat <[email protected]>

This patch adds support to discover Heart Rate Service characteristics when
connected to remote device.

---
profiles/heartrate/heartrate.c | 49 +++++++++++++++++++++++++++++++++++++++++-
profiles/heartrate/heartrate.h | 3 ++-
profiles/heartrate/manager.c | 22 ++++++++++++++++++-
3 files changed, 71 insertions(+), 3 deletions(-)

diff --git a/profiles/heartrate/heartrate.c b/profiles/heartrate/heartrate.c
index 918546e..e4147cf 100644
--- a/profiles/heartrate/heartrate.c
+++ b/profiles/heartrate/heartrate.c
@@ -48,6 +48,13 @@ struct heartrate {
struct heartrate_adapter *hra;
GAttrib *attrib;
guint attioid;
+ struct att_range *svc_range;
+ GSList *chars;
+};
+
+struct characteristic {
+ struct gatt_char attr;
+ struct heartrate *hr;
};

static GSList *heartrate_adapters = NULL;
@@ -95,7 +102,11 @@ static void destroy_heartrate_device(gpointer user_data)
if (hr->attrib != NULL)
g_attrib_unref(hr->attrib);

+ if (hr->chars != NULL)
+ g_slist_free_full(hr->chars, g_free);
+
btd_device_unref(hr->dev);
+ g_free(hr->svc_range);
g_free(hr);
}

@@ -109,6 +120,33 @@ static void destroy_heartrate_adapter(gpointer user_data)
g_free(hra);
}

+static void configure_heartrate_cb(GSList *characteristics, guint8 status,
+ gpointer user_data)
+{
+ struct heartrate *hr = user_data;
+ GSList *l;
+
+ if (status != 0) {
+ error("Discover Heart Rate characteristics: %s",
+ att_ecode2str(status));
+ return;
+ }
+
+ for (l = characteristics; l; l = l->next) {
+ struct gatt_char *c = l->data;
+ struct characteristic *ch;
+
+ ch = g_new0(struct characteristic, 1);
+ ch->attr.handle = c->handle;
+ ch->attr.properties = c->properties;
+ ch->attr.value_handle = c->value_handle;
+ memcpy(ch->attr.uuid, c->uuid, MAX_LEN_UUID_STR + 1);
+ ch->hr = hr;
+
+ hr->chars = g_slist_append(hr->chars, ch);
+ }
+}
+
static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
{
struct heartrate *hr = user_data;
@@ -116,6 +154,10 @@ static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
DBG("GATT Connected");

hr->attrib = g_attrib_ref(attrib);
+
+ gatt_discover_char(hr->attrib, hr->svc_range->start,
+ hr->svc_range->end, NULL,
+ configure_heartrate_cb, hr);
}

static void attio_disconnected_cb(gpointer user_data)
@@ -153,7 +195,8 @@ void heartrate_adapter_unregister(struct btd_adapter *adapter)
destroy_heartrate_adapter(hra);
}

-int heartrate_device_register(struct btd_device *device)
+int heartrate_device_register(struct btd_device *device,
+ struct gatt_primary *pattr)
{
struct heartrate_adapter *hra;
struct btd_adapter *adapter;
@@ -172,6 +215,10 @@ int heartrate_device_register(struct btd_device *device)

hra->devices = g_slist_prepend(hra->devices, hr);

+ hr->svc_range = g_new0(struct att_range, 1);
+ hr->svc_range->start = pattr->range.start;
+ hr->svc_range->end = pattr->range.end;
+
hr->attioid = btd_device_add_attio_callback(device,
attio_connected_cb,
attio_disconnected_cb,
diff --git a/profiles/heartrate/heartrate.h b/profiles/heartrate/heartrate.h
index 486f5b3..ec3ed7a 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 *pattr);
void heartrate_device_unregister(struct btd_device *device);
diff --git a/profiles/heartrate/manager.c b/profiles/heartrate/manager.c
index 9d2446c..ff2da21 100644
--- a/profiles/heartrate/manager.c
+++ b/profiles/heartrate/manager.c
@@ -33,6 +33,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);
@@ -45,7 +53,19 @@ 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);
+ struct gatt_primary *pattr;
+ GSList *primaries, *l;
+
+ primaries = btd_device_get_primaries(device);
+
+ l = g_slist_find_custom(primaries, HEART_RATE_UUID,
+ primary_uuid_cmp);
+ if (l == NULL)
+ return -EINVAL;
+
+ pattr = l->data;
+
+ return heartrate_device_register(device, pattr);
}

static void heartrate_device_remove(struct btd_device *device)
--
1.7.11.3


2012-09-06 11:32:40

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [PATCH v3 06/17] heartrate: Discover characteristics descriptors

From: Rafal Garbat <[email protected]>

---
profiles/heartrate/heartrate.c | 72 +++++++++++++++++++++++++++++++++++++++++-
1 file changed, 71 insertions(+), 1 deletion(-)

diff --git a/profiles/heartrate/heartrate.c b/profiles/heartrate/heartrate.c
index 02a4617..69d6d51 100644
--- a/profiles/heartrate/heartrate.c
+++ b/profiles/heartrate/heartrate.c
@@ -55,6 +55,13 @@ struct heartrate {
struct characteristic {
struct gatt_char attr;
struct heartrate *hr;
+ GSList *desc;
+};
+
+struct descriptor {
+ struct characteristic *ch;
+ uint16_t handle;
+ bt_uuid_t uuid;
};

static GSList *heartrate_adapters = NULL;
@@ -92,6 +99,15 @@ find_heartrate_adapter(struct btd_adapter *adapter)
return l->data;
}

+static void destroy_char(gpointer user_data)
+{
+ struct characteristic *c = user_data;
+
+ g_slist_free_full(c->desc, g_free);
+
+ g_free(c);
+}
+
static void destroy_heartrate_device(gpointer user_data)
{
struct heartrate *hr = user_data;
@@ -103,7 +119,7 @@ static void destroy_heartrate_device(gpointer user_data)
g_attrib_unref(hr->attrib);

if (hr->chars != NULL)
- g_slist_free_full(hr->chars, g_free);
+ g_slist_free_full(hr->chars, destroy_char);

btd_device_unref(hr->dev);
g_free(hr->svc_range);
@@ -128,6 +144,44 @@ static void process_heartrate_char(struct characteristic *ch)
DBG("Body Sensor Location supported by client");
}

+static void discover_desc_cb(guint8 status, const guint8 *pdu,
+ guint16 len, gpointer user_data)
+{
+ struct characteristic *ch = user_data;
+ struct att_data_list *list;
+ uint8_t format;
+ int i;
+
+ if (status != 0) {
+ error("Discover all characteristic descriptors failed [%s]: %s",
+ ch->attr.uuid, att_ecode2str(status));
+ return;
+ }
+
+ list = dec_find_info_resp(pdu, len, &format);
+ if (list == NULL)
+ return;
+
+ for (i = 0; i < list->num; i++) {
+ struct descriptor *desc;
+ uint8_t *value;
+
+ value = list->data[i];
+ desc = g_new0(struct descriptor, 1);
+ desc->handle = att_get_u16(value);
+ desc->ch = ch;
+
+ if (format == 0x01)
+ desc->uuid = att_get_uuid16(value + 2);
+ else
+ desc->uuid = att_get_uuid128(value + 2);
+
+ ch->desc = g_slist_append(ch->desc, desc);
+ }
+
+ att_data_list_free(list);
+}
+
static void configure_heartrate_cb(GSList *characteristics, guint8 status,
gpointer user_data)
{
@@ -143,6 +197,7 @@ static void configure_heartrate_cb(GSList *characteristics, guint8 status,
for (l = characteristics; l; l = l->next) {
struct gatt_char *c = l->data;
struct characteristic *ch;
+ uint16_t start, end;

ch = g_new0(struct characteristic, 1);
ch->attr.handle = c->handle;
@@ -154,6 +209,21 @@ static void configure_heartrate_cb(GSList *characteristics, guint8 status,
hr->chars = g_slist_append(hr->chars, ch);

process_heartrate_char(ch);
+
+ start = c->value_handle + 1;
+
+ if (l->next != NULL) {
+ struct gatt_char *c = l->next->data;
+ if (start == c->handle)
+ continue;
+ end = c->handle - 1;
+ } else if (c->value_handle != hr->svc_range->end) {
+ end = hr->svc_range->end;
+ } else {
+ continue;
+ }
+
+ gatt_find_info(hr->attrib, start, end, discover_desc_cb, ch);
}
}

--
1.7.11.3


2012-09-06 11:32:39

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [PATCH v3 05/17] heartrate: Process characteristics

From: Rafal Garbat <[email protected]>

This patch adds stub to process discovered characteristics.

---
lib/uuid.h | 2 ++
profiles/heartrate/heartrate.c | 10 ++++++++++
2 files changed, 12 insertions(+)

diff --git a/lib/uuid.h b/lib/uuid.h
index 3488e66..0a9db51 100644
--- a/lib/uuid.h
+++ b/lib/uuid.h
@@ -64,6 +64,8 @@ extern "C" {
#define SAP_UUID "0000112D-0000-1000-8000-00805f9b34fb"

#define HEART_RATE_UUID "0000180d-0000-1000-8000-00805f9b34fb"
+#define HEART_RATE_CONTROL_POINT_UUID "00002a39-0000-1000-8000-00805f9b34fb"
+#define BODY_SENSOR_LOCATION_UUID "00002a38-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 e4147cf..02a4617 100644
--- a/profiles/heartrate/heartrate.c
+++ b/profiles/heartrate/heartrate.c
@@ -120,6 +120,14 @@ static void destroy_heartrate_adapter(gpointer user_data)
g_free(hra);
}

+static void process_heartrate_char(struct characteristic *ch)
+{
+ if (g_strcmp0(ch->attr.uuid, HEART_RATE_CONTROL_POINT_UUID) == 0)
+ DBG("Heart Rate Control Point supported by client");
+ else if (g_strcmp0(ch->attr.uuid, BODY_SENSOR_LOCATION_UUID) == 0)
+ DBG("Body Sensor Location supported by client");
+}
+
static void configure_heartrate_cb(GSList *characteristics, guint8 status,
gpointer user_data)
{
@@ -144,6 +152,8 @@ static void configure_heartrate_cb(GSList *characteristics, guint8 status,
ch->hr = hr;

hr->chars = g_slist_append(hr->chars, ch);
+
+ process_heartrate_char(ch);
}
}

--
1.7.11.3


2012-09-06 11:32:36

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [PATCH v3 02/17] heartrate: Add Heart Rate Profile client

From: Rafal Garbat <[email protected]>

This patch adds initial support for the Heart Rate Profile client.

---
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 | 75 +++++++++++++++++++
profiles/heartrate/manager.h | 24 ++++++
7 files changed, 351 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 4977a05..cb36d91 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -212,7 +212,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 \
@@ -241,7 +241,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..17514c0
--- /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 *hra;
+};
+
+static GSList *heartrate_adapters = NULL;
+
+static gint cmp_adapter(gconstpointer a, gconstpointer b)
+{
+ const struct heartrate_adapter *hra = a;
+ const struct btd_adapter *adapter = b;
+
+ if (adapter == hra->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_device(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 *hra = user_data;
+
+ g_free(hra);
+}
+
+int heartrate_adapter_register(struct btd_adapter *adapter)
+{
+ struct heartrate_adapter *hra;
+
+ hra = g_new0(struct heartrate_adapter, 1);
+ hra->adapter = adapter;
+
+ heartrate_adapters = g_slist_prepend(heartrate_adapters, hra);
+
+ return 0;
+}
+
+void heartrate_adapter_unregister(struct btd_adapter *adapter)
+{
+ struct heartrate_adapter *hra;
+
+ hra = find_heartrate_adapter(adapter);
+ if (hra == NULL)
+ return;
+
+ heartrate_adapters = g_slist_remove(heartrate_adapters, hra);
+
+ destroy_heartrate_adapter(hra);
+}
+
+int heartrate_device_register(struct btd_device *device)
+{
+ struct heartrate_adapter *hra;
+ struct btd_adapter *adapter;
+ struct heartrate *hr;
+
+ adapter = device_get_adapter(device);
+
+ hra = find_heartrate_adapter(adapter);
+
+ if (hra == NULL)
+ return -1;
+
+ hr = g_new0(struct heartrate, 1);
+ hr->dev = btd_device_ref(device);
+ hr->hra = hra;
+
+ hra->devices = g_slist_prepend(hra->devices, hr);
+
+ return 0;
+}
+
+void heartrate_device_unregister(struct btd_device *device)
+{
+ struct heartrate_adapter *hra;
+ struct btd_adapter *adapter;
+ struct heartrate *hr;
+ GSList *l;
+
+ adapter = device_get_adapter(device);
+
+ hra = find_heartrate_adapter(adapter);
+ if (hra == NULL)
+ return;
+
+ l = g_slist_find_custom(hra->devices, device, cmp_device);
+ if (l == NULL)
+ return;
+
+ hr = l->data;
+
+ hra->devices = g_slist_remove(hra->devices, hr);
+
+ destroy_heartrate_device(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..9d2446c
--- /dev/null
+++ b/profiles/heartrate/manager.c
@@ -0,0 +1,75 @@
+/*
+ *
+ * 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 "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-06 11:32:37

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [PATCH v3 03/17] heartrate: Add attio callbacks

---
profiles/heartrate/heartrate.c | 40 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 40 insertions(+)

diff --git a/profiles/heartrate/heartrate.c b/profiles/heartrate/heartrate.c
index 17514c0..918546e 100644
--- a/profiles/heartrate/heartrate.c
+++ b/profiles/heartrate/heartrate.c
@@ -31,7 +31,12 @@

#include "adapter.h"
#include "device.h"
+#include "gattrib.h"
+#include "attio.h"
+#include "att.h"
+#include "gatt.h"
#include "heartrate.h"
+#include "log.h"

struct heartrate_adapter {
struct btd_adapter *adapter;
@@ -41,6 +46,8 @@ struct heartrate_adapter {
struct heartrate {
struct btd_device *dev;
struct heartrate_adapter *hra;
+ GAttrib *attrib;
+ guint attioid;
};

static GSList *heartrate_adapters = NULL;
@@ -82,6 +89,12 @@ static void destroy_heartrate_device(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);
}
@@ -90,9 +103,31 @@ static void destroy_heartrate_adapter(gpointer user_data)
{
struct heartrate_adapter *hra = user_data;

+ if (hra->devices != NULL)
+ g_slist_free_full(hra->devices, destroy_heartrate_device);
+
g_free(hra);
}

+static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
+{
+ struct heartrate *hr = user_data;
+
+ DBG("GATT Connected");
+
+ hr->attrib = g_attrib_ref(attrib);
+}
+
+static void attio_disconnected_cb(gpointer user_data)
+{
+ struct heartrate *hr = user_data;
+
+ DBG("GATT Disconnected");
+
+ g_attrib_unref(hr->attrib);
+ hr->attrib = NULL;
+}
+
int heartrate_adapter_register(struct btd_adapter *adapter)
{
struct heartrate_adapter *hra;
@@ -137,6 +172,11 @@ int heartrate_device_register(struct btd_device *device)

hra->devices = g_slist_prepend(hra->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-06 11:32:35

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [PATCH v3 01/17] Heart Rate Profile 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..4ce96d8
--- /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