2012-12-04 12:43:26

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [PATCH v3 00/20] CSCP plugin

Hi,

Changes since v2:
- rebased to latest upstream master
- moved all code to cyclingspeed.c to avoid "empty" calls across files

No changes in functionality.


Andrzej Kaczmarek (20):
cyclingspeed: Add CSC profile plugin skeleton
cyclingspeed: Add attio callbacks
cyclingspeed: Discover CSCS characteristics
cyclingspeed: Discover characteristics CCC
cyclingspeed: Read CSC Feature characteristic value
cyclingspeed: Read Sensor Location characteristic value
cyclingspeed: Add CyclingSpeedManager interface
cyclingspeed: Add support to enable measurement notifications
cyclingspeed: Process measurement notifications
cyclingspeed: Add DBus.Properties for org.bluez.CyclingSpeed
interface
cyclingspeed: Add stub to use SC Control Point
cyclingspeed: Add support for Request Supported Sensor Locations
cyclingspeed: Add support for Update Sensor Location
cyclingspeed: Add support for Set Cumulative Value
core: Add CyclingSpeedWatcher interface to default policy
doc: Remove Get-/SetProperties from CSC API document
doc: Rename cycling API to cyclingspeed
build: Add CSCP API document to EXTRA_DIST
test: Add cyclingspeed test script
test: Enable speed and cadence calculation in test-cyclingspeed

Makefile.am | 8 +-
Makefile.tools | 3 +-
doc/cycling-api.txt | 118 ----
doc/cyclingspeed-api.txt | 100 +++
lib/uuid.h | 6 +
profiles/cyclingspeed/cyclingspeed.c | 1285 ++++++++++++++++++++++++++++++++++
src/bluetooth.conf | 1 +
test/test-cyclingspeed | 191 +++++
8 files changed, 1590 insertions(+), 122 deletions(-)
delete mode 100644 doc/cycling-api.txt
create mode 100644 doc/cyclingspeed-api.txt
create mode 100644 profiles/cyclingspeed/cyclingspeed.c
create mode 100755 test/test-cyclingspeed

--
1.8.0



2012-12-05 08:23:39

by Johan Hedberg

[permalink] [raw]
Subject: Re: [PATCH v3 00/20] CSCP plugin

Hi Andrzej,

On Tue, Dec 04, 2012, Andrzej Kaczmarek wrote:
> Changes since v2:
> - rebased to latest upstream master
> - moved all code to cyclingspeed.c to avoid "empty" calls across files
>
> No changes in functionality.
>
>
> Andrzej Kaczmarek (20):
> cyclingspeed: Add CSC profile plugin skeleton
> cyclingspeed: Add attio callbacks
> cyclingspeed: Discover CSCS characteristics
> cyclingspeed: Discover characteristics CCC
> cyclingspeed: Read CSC Feature characteristic value
> cyclingspeed: Read Sensor Location characteristic value
> cyclingspeed: Add CyclingSpeedManager interface
> cyclingspeed: Add support to enable measurement notifications
> cyclingspeed: Process measurement notifications
> cyclingspeed: Add DBus.Properties for org.bluez.CyclingSpeed
> interface
> cyclingspeed: Add stub to use SC Control Point
> cyclingspeed: Add support for Request Supported Sensor Locations
> cyclingspeed: Add support for Update Sensor Location
> cyclingspeed: Add support for Set Cumulative Value
> core: Add CyclingSpeedWatcher interface to default policy
> doc: Remove Get-/SetProperties from CSC API document
> doc: Rename cycling API to cyclingspeed
> build: Add CSCP API document to EXTRA_DIST
> test: Add cyclingspeed test script
> test: Enable speed and cadence calculation in test-cyclingspeed
>
> Makefile.am | 8 +-
> Makefile.tools | 3 +-
> doc/cycling-api.txt | 118 ----
> doc/cyclingspeed-api.txt | 100 +++
> lib/uuid.h | 6 +
> profiles/cyclingspeed/cyclingspeed.c | 1285 ++++++++++++++++++++++++++++++++++
> src/bluetooth.conf | 1 +
> test/test-cyclingspeed | 191 +++++
> 8 files changed, 1590 insertions(+), 122 deletions(-)
> delete mode 100644 doc/cycling-api.txt
> create mode 100644 doc/cyclingspeed-api.txt
> create mode 100644 profiles/cyclingspeed/cyclingspeed.c
> create mode 100755 test/test-cyclingspeed

All patches in this set have been applied. Thanks.

I also pushed an additional coding style cleanup patch, so if you have
further patches to this code queued up please rebase first to the
upstream tree.

Johan

2012-12-04 12:43:46

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [PATCH v3 20/20] test: Enable speed and cadence calculation in test-cyclingspeed

This patch allows to enable instantenous speed and cadence calculation
based on measurement received from remote device.

To enable calculations additional parameter "--circumference <value>"
needs to be used when calling script which sets wheel circumference in mm.

Both speed and cadence are enabled at the same time. Speed is calculated
in km/h and cadence in rpm.
---
test/test-cyclingspeed | 71 ++++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 69 insertions(+), 2 deletions(-)

diff --git a/test/test-cyclingspeed b/test/test-cyclingspeed
index 92fe935..841456c 100755
--- a/test/test-cyclingspeed
+++ b/test/test-cyclingspeed
@@ -14,23 +14,86 @@ import dbus.service
import dbus.mainloop.glib
from optparse import OptionParser, make_option

+class MeasurementQ:
+ def __init__(self, wrap_v):
+ self._now = [None, None]
+ self._prev = [None, None]
+ self._wrap_v = wrap_v
+
+ def can_calc(self):
+ return ((self._now[0] is not None)
+ and (self._now[1] is not None)
+ and (self._prev[0] is not None)
+ and (self._prev[1] is not None))
+
+ def delta_v(self):
+ delta = self._now[0] - self._prev[0]
+ if (delta < 0) and (self._wrap_v):
+ delta = delta + 65536
+ return delta
+
+ def delta_t(self):
+ delta = self._now[1] - self._prev[1]
+ if delta < 0:
+ delta = delta + 65536
+ return delta
+
+ def put(self, data):
+ self._prev = self._now
+ self._now = data
+
class Watcher(dbus.service.Object):
+ _wheel = MeasurementQ(False)
+ _crank = MeasurementQ(True)
+ _circumference = None
+
+ def enable_calc(self, v):
+ self._circumference = v
+
@dbus.service.method("org.bluez.CyclingSpeedWatcher",
in_signature="oa{sv}", out_signature="")
def MeasurementReceived(self, device, measure):
print("Measurement received from %s" % device)

+ rev = None
+ evt = None
if "WheelRevolutions" in measure:
+ rev = measure["WheelRevolutions"]
print("WheelRevolutions: ", measure["WheelRevolutions"])
-
if "LastWheelEventTime" in measure:
+ evt = measure["LastWheelEventTime"]
print("LastWheelEventTime: ", measure["LastWheelEventTime"])
+ self._wheel.put( [rev, evt] )

+ rev = None
+ evt = None
if "CrankRevolutions" in measure:
+ rev = measure["CrankRevolutions"]
print("CrankRevolutions: ", measure["CrankRevolutions"])
-
if "LastCrankEventTime" in measure:
+ evt = measure["LastCrankEventTime"]
print("LastCrankEventTime: ", measure["LastCrankEventTime"])
+ self._crank.put( [rev, evt] )
+
+ if self._circumference is None:
+ return
+
+ if self._wheel.can_calc():
+ delta_v = self._wheel.delta_v()
+ delta_t = self._wheel.delta_t()
+
+ if (delta_v >= 0) and (delta_t > 0):
+ speed = delta_v * self._circumference * 1024 / delta_t # mm/s
+ speed = speed * 0.0036 # mm/s -> km/h
+ print("(calculated) Speed: %.2f km/h" % speed)
+
+ if self._crank.can_calc():
+ delta_v = self._crank.delta_v()
+ delta_t = self._crank.delta_t()
+
+ if delta_t > 0:
+ cadence = delta_v * 1024 / delta_t
+ print("(calculated) Cadence: %d rpm" % cadence)

def properties_changed(interface, changed, invalidated):
if "Location" in changed:
@@ -46,6 +109,8 @@ if __name__ == "__main__":
type="string", dest="adapter"),
make_option("-b", "--device", action="store",
type="string", dest="address"),
+ make_option("-c", "--circumference", action="store",
+ type="int", dest="circumference"),
]

parser = OptionParser(option_list=option_list)
@@ -77,6 +142,8 @@ if __name__ == "__main__":

watcher_path = "/test/watcher"
watcher = Watcher(bus, watcher_path)
+ if options.circumference:
+ watcher.enable_calc(options.circumference)
cscmanager.RegisterWatcher(watcher_path)

csc = dbus.Interface(bus.get_object("org.bluez", device_path),
--
1.8.0


2012-12-04 12:43:36

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [PATCH v3 10/20] cyclingspeed: Add DBus.Properties for org.bluez.CyclingSpeed interface

---
profiles/cyclingspeed/cyclingspeed.c | 96 +++++++++++++++++++++++++++++++++++-
1 file changed, 95 insertions(+), 1 deletion(-)

diff --git a/profiles/cyclingspeed/cyclingspeed.c b/profiles/cyclingspeed/cyclingspeed.c
index a3e2d61..eace558 100644
--- a/profiles/cyclingspeed/cyclingspeed.c
+++ b/profiles/cyclingspeed/cyclingspeed.c
@@ -104,6 +104,21 @@ struct characteristic {

static GSList *csc_adapters = NULL;

+static const char * const location_enum[] = {
+ "other", "top-of-shoe", "in-shoe", "hip", "front-wheel", "left-crank",
+ "right-crank", "left-pedal", "right-pedal", "front-hub",
+ "rear-dropout", "chainstay", "rear-wheel", "rear-hub"
+};
+
+static const gchar *location2str(uint8_t value)
+{
+ if (value < G_N_ELEMENTS(location_enum))
+ return location_enum[value];
+
+ info("Body Sensor Location [%d] is RFU", value);
+ return location_enum[0];
+}
+
static gint cmp_adapter(gconstpointer a, gconstpointer b)
{
const struct csc_adapter *cadapter = a;
@@ -694,6 +709,72 @@ static void csc_adapter_remove(struct btd_profile *p,
CYCLINGSPEED_MANAGER_INTERFACE);
}

+static gboolean property_get_location(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct csc *csc = data;
+ const char *loc;
+
+ if (!csc->has_location)
+ return FALSE;
+
+ loc = location2str(csc->location);
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &loc);
+
+ return TRUE;
+}
+
+static gboolean property_exists_location(const GDBusPropertyTable *property,
+ void *data)
+{
+ struct csc *csc = data;
+
+ return csc->has_location;
+}
+
+static gboolean property_exists_locations(const GDBusPropertyTable *property,
+ void *data)
+{
+ struct csc *csc = data;
+
+ return !!(csc->feature & MULTI_SENSOR_LOC_SUPPORT);
+}
+
+static gboolean property_get_wheel_rev_sup(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct csc *csc = data;
+ dbus_bool_t val;
+
+ val = !!(csc->feature & WHEEL_REV_SUPPORT);
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &val);
+
+ return TRUE;
+}
+
+static gboolean property_get_multi_loc_sup(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct csc *csc = data;
+ dbus_bool_t val;
+
+ val = !!(csc->feature & MULTI_SENSOR_LOC_SUPPORT);
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &val);
+
+ return TRUE;
+}
+
+static const GDBusPropertyTable cyclingspeed_device_properties[] = {
+ { "Location", "s", property_get_location, NULL,
+ property_exists_location },
+ { "SupportedLocations", "as", NULL, NULL,
+ property_exists_locations },
+ { "WheelRevolutionDataSupported", "b", property_get_wheel_rev_sup },
+ { "MultipleLocationsSupported", "b", property_get_multi_loc_sup },
+ { }
+};
+
static gint cmp_primary_uuid(gconstpointer a, gconstpointer b)
{
const struct gatt_primary *prim = a;
@@ -730,6 +811,18 @@ static int csc_device_probe(struct btd_profile *p,
csc->dev = btd_device_ref(device);
csc->cadapter = cadapter;

+ if (!g_dbus_register_interface(btd_get_dbus_connection(),
+ device_get_path(device),
+ CYCLINGSPEED_INTERFACE,
+ NULL, NULL,
+ cyclingspeed_device_properties,
+ csc, destroy_csc)) {
+ error("D-Bus failed to register %s interface",
+ CYCLINGSPEED_INTERFACE);
+ destroy_csc(csc);
+ return -EIO;
+ }
+
csc->svc_range = g_new0(struct att_range, 1);
csc->svc_range->start = prim->range.start;
csc->svc_range->end = prim->range.end;
@@ -764,7 +857,8 @@ static void csc_device_remove(struct btd_profile *p,

cadapter->devices = g_slist_remove(cadapter->devices, csc);

- destroy_csc(csc);
+ g_dbus_unregister_interface(btd_get_dbus_connection(),
+ device_get_path(device), CYCLINGSPEED_INTERFACE);
}

static struct btd_profile cscp_profile = {
--
1.8.0


2012-12-04 12:43:45

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [PATCH v3 19/20] test: Add cyclingspeed test script

---
Makefile.tools | 3 +-
test/test-cyclingspeed | 124 +++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 126 insertions(+), 1 deletion(-)
create mode 100755 test/test-cyclingspeed

diff --git a/Makefile.tools b/Makefile.tools
index 689b4d2..ded9faf 100644
--- a/Makefile.tools
+++ b/Makefile.tools
@@ -209,4 +209,5 @@ EXTRA_DIST += test/sap_client.py test/hsplay test/hsmicro \
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 \
- test/test-heartrate test/test-alert test/test-hfp
+ test/test-heartrate test/test-alert test/test-hfp \
+ test/test-cycling
diff --git a/test/test-cyclingspeed b/test/test-cyclingspeed
new file mode 100755
index 0000000..92fe935
--- /dev/null
+++ b/test/test-cyclingspeed
@@ -0,0 +1,124 @@
+#!/usr/bin/python
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+'''
+Cycling Speed and Cadence 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.CyclingSpeedWatcher",
+ in_signature="oa{sv}", out_signature="")
+ def MeasurementReceived(self, device, measure):
+ print("Measurement received from %s" % device)
+
+ if "WheelRevolutions" in measure:
+ print("WheelRevolutions: ", measure["WheelRevolutions"])
+
+ if "LastWheelEventTime" in measure:
+ print("LastWheelEventTime: ", measure["LastWheelEventTime"])
+
+ if "CrankRevolutions" in measure:
+ print("CrankRevolutions: ", measure["CrankRevolutions"])
+
+ if "LastCrankEventTime" in measure:
+ print("LastCrankEventTime: ", measure["LastCrankEventTime"])
+
+def properties_changed(interface, changed, invalidated):
+ if "Location" in changed:
+ print("Sensor location: %s" % changed["Location"])
+
+if __name__ == "__main__":
+ dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+ bus = dbus.SystemBus()
+
+ 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> [-c <value>] [cmd]" % (sys.argv[0]))
+ print("Possible commands:")
+ print("\tShowSupportedLocations")
+ print("\tSetLocation <location>")
+ print("\tSetCumulativeWheelRevolutions <value>")
+ sys.exit(1)
+
+ manager = dbus.Interface(bus.get_object("org.bluez", "/"),
+ "org.bluez.Manager")
+ 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")
+
+ device_path = adapter.FindDevice(options.address)
+
+ cscmanager = dbus.Interface(bus.get_object("org.bluez", adapter_path),
+ "org.bluez.CyclingSpeedManager")
+
+ watcher_path = "/test/watcher"
+ watcher = Watcher(bus, watcher_path)
+ cscmanager.RegisterWatcher(watcher_path)
+
+ csc = dbus.Interface(bus.get_object("org.bluez", device_path),
+ "org.bluez.CyclingSpeed")
+
+ bus.add_signal_receiver(properties_changed, bus_name="org.bluez",
+ path=device_path,
+ dbus_interface="org.freedesktop.DBus.Properties",
+ signal_name="PropertiesChanged")
+
+ device_prop = dbus.Interface(bus.get_object("org.bluez", device_path),
+ "org.freedesktop.DBus.Properties")
+
+ properties = device_prop.GetAll("org.bluez.CyclingSpeed")
+
+ if "Location" in properties:
+ print("Sensor location: %s" % properties["Location"])
+ else:
+ print("Sensor location is not supported")
+
+ if len(args) > 0:
+ if args[0] == "ShowSupportedLocations":
+ if properties["MultipleSensorLocationsSupported"]:
+ print("Supported locations: ", properties["SupportedLocations"])
+ else:
+ print("Multiple sensor locations not supported")
+
+ elif args[0] == "SetLocation":
+ if properties["MultipleSensorLocationsSupported"]:
+ device_prop.Set("org.bluez.CyclingSpeed", "Location", args[1])
+ else:
+ print("Multiple sensor locations not supported")
+
+ elif args[0] == "SetCumulativeWheelRevolutions":
+ if properties["WheelRevolutionDataSupported"]:
+ csc.SetCumulativeWheelRevolutions(dbus.UInt32(args[1]))
+ else:
+ print("Wheel revolution data not supported")
+
+ else:
+ print("Unknown command")
+ sys.exit(1)
+
+ mainloop = gobject.MainLoop()
+ mainloop.run()
--
1.8.0


2012-12-04 12:43:39

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [PATCH v3 13/20] cyclingspeed: Add support for Update Sensor Location

---
profiles/cyclingspeed/cyclingspeed.c | 122 ++++++++++++++++++++++++++++++++++-
1 file changed, 121 insertions(+), 1 deletion(-)

diff --git a/profiles/cyclingspeed/cyclingspeed.c b/profiles/cyclingspeed/cyclingspeed.c
index 8283894..58ccf74 100644
--- a/profiles/cyclingspeed/cyclingspeed.c
+++ b/profiles/cyclingspeed/cyclingspeed.c
@@ -75,6 +75,9 @@ struct controlpoint_req {
struct csc *csc;
uint8_t opcode;
guint timeout;
+ GDBusPendingReply reply_id;
+
+ uint8_t pending_location;
};

struct csc_adapter {
@@ -149,6 +152,17 @@ static const gchar *location2str(uint8_t value)
return location_enum[0];
}

+static int str2location(const char *location)
+{
+ size_t i;
+
+ for (i = 0; i < G_N_ELEMENTS(location_enum); i++)
+ if (!strcmp(location_enum[i], location))
+ return i;
+
+ return -1;
+}
+
static gint cmp_adapter(gconstpointer a, gconstpointer b)
{
const struct csc_adapter *cadapter = a;
@@ -272,6 +286,12 @@ static gboolean controlpoint_timeout(gpointer user_data)
{
struct controlpoint_req *req = user_data;

+ if (req->opcode == UPDATE_SENSOR_LOC) {
+ g_dbus_pending_property_error(req->reply_id,
+ ERROR_INTERFACE ".Failed",
+ "Operation failed (timeout)");
+ }
+
req->csc->pending_req = NULL;
g_free(req);

@@ -286,6 +306,12 @@ static void controlpoint_write_cb(guint8 status, const guint8 *pdu, guint16 len,
if (status != 0) {
error("SC Control Point write failed (opcode=%d)", req->opcode);

+ if (req->opcode == UPDATE_SENSOR_LOC) {
+ g_dbus_pending_property_error(req->reply_id,
+ ERROR_INTERFACE ".Failed",
+ "Operation failed (%d)", status);
+ }
+
req->csc->pending_req = NULL;
g_free(req);

@@ -564,6 +590,40 @@ static void measurement_notify_handler(const uint8_t *pdu, uint16_t len,
process_measurement(csc, pdu + 3, len - 3);
}

+static void controlpoint_property_reply(struct controlpoint_req *req,
+ uint8_t code)
+{
+ switch (code) {
+ case RSP_SUCCESS:
+ g_dbus_pending_property_success(req->reply_id);
+ break;
+
+ case RSP_NOT_SUPPORTED:
+ g_dbus_pending_property_error(req->reply_id,
+ ERROR_INTERFACE ".NotSupported",
+ "Feature is not supported");
+ break;
+
+ case RSP_INVALID_PARAM:
+ g_dbus_pending_property_error(req->reply_id,
+ ERROR_INTERFACE ".InvalidArguments",
+ "Invalid arguments in method call");
+ break;
+
+ case RSP_FAILED:
+ g_dbus_pending_property_error(req->reply_id,
+ ERROR_INTERFACE ".Failed",
+ "Operation failed");
+ break;
+
+ default:
+ g_dbus_pending_property_error(req->reply_id,
+ ERROR_INTERFACE ".Failed",
+ "Operation failed (%d)", code);
+ break;
+ }
+}
+
static void controlpoint_ind_handler(const uint8_t *pdu, uint16_t len,
gpointer user_data)
{
@@ -623,6 +683,16 @@ static void controlpoint_ind_handler(const uint8_t *pdu, uint16_t len,
error("Failed to read Supported Sendor Locations");
}
break;
+
+ case UPDATE_SENSOR_LOC:
+ csc->location = req->pending_location;
+
+ controlpoint_property_reply(req, rsp_code);
+
+ g_dbus_emit_property_changed(btd_get_dbus_connection(),
+ device_get_path(csc->dev),
+ CYCLINGSPEED_INTERFACE, "Location");
+ break;
}

csc->pending_req = NULL;
@@ -890,6 +960,56 @@ static gboolean property_get_location(const GDBusPropertyTable *property,
return TRUE;
}

+static void property_set_location(const GDBusPropertyTable *property,
+ DBusMessageIter *iter,
+ GDBusPendingPropertySet id, void *data)
+{
+ struct csc *csc = data;
+ char *loc;
+ int loc_val;
+ uint8_t att_val[2];
+ struct controlpoint_req *req;
+
+ if (csc->pending_req != NULL)
+ return g_dbus_pending_property_error(id,
+ ERROR_INTERFACE ".InProgress",
+ "Operation already in progress");
+
+ if (!(csc->feature & MULTI_SENSOR_LOC_SUPPORT))
+ return g_dbus_pending_property_error(id,
+ ERROR_INTERFACE ".NotSupported",
+ "Feature is not supported");
+
+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING)
+ return g_dbus_pending_property_error(id,
+ ERROR_INTERFACE ".InvalidArguments",
+ "Invalid arguments in method call");
+
+ dbus_message_iter_get_basic(iter, &loc);
+
+ loc_val = str2location(loc);
+
+ if (loc_val < 0)
+ return g_dbus_pending_property_error(id,
+ ERROR_INTERFACE ".InvalidArguments",
+ "Invalid arguments in method call");
+
+ req = g_new(struct controlpoint_req, 1);
+ req->csc = csc;
+ req->reply_id = id;
+ req->opcode = UPDATE_SENSOR_LOC;
+ req->pending_location = loc_val;
+
+ csc->pending_req = req;
+
+ att_val[0] = UPDATE_SENSOR_LOC;
+ att_val[1] = loc_val;
+
+ gatt_write_char(csc->attrib, csc->controlpoint_val_handle, att_val,
+ sizeof(att_val), controlpoint_write_cb, req);
+
+}
+
static gboolean property_exists_location(const GDBusPropertyTable *property,
void *data)
{
@@ -954,7 +1074,7 @@ static gboolean property_get_multi_loc_sup(const GDBusPropertyTable *property,
}

static const GDBusPropertyTable cyclingspeed_device_properties[] = {
- { "Location", "s", property_get_location, NULL,
+ { "Location", "s", property_get_location, property_set_location,
property_exists_location },
{ "SupportedLocations", "as", property_get_locations, NULL,
property_exists_locations },
--
1.8.0


2012-12-04 12:43:44

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [PATCH v3 18/20] build: Add CSCP API document to EXTRA_DIST

---
Makefile.am | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/Makefile.am b/Makefile.am
index d009828..2e0e9b2 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -342,7 +342,8 @@ EXTRA_DIST += doc/manager-api.txt \
doc/sap-api.txt doc/media-api.txt doc/assigned-numbers.txt \
doc/supported-features.txt doc/alert-api.txt doc/mgmt-api.txt \
doc/oob-api.txt doc/proximity-api.txt doc/heartrate-api.txt \
- doc/thermometer-api.txt doc/audio-telephony-design.txt
+ doc/thermometer-api.txt doc/audio-telephony-design.txt \
+ doc/cyclingspeed-api.txt

AM_CFLAGS += @DBUS_CFLAGS@ @GLIB_CFLAGS@

--
1.8.0


2012-12-04 12:43:43

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [PATCH v3 17/20] doc: Rename cycling API to cyclingspeed

---
doc/cycling-api.txt | 100 -----------------------------------------------
doc/cyclingspeed-api.txt | 100 +++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 100 insertions(+), 100 deletions(-)
delete mode 100644 doc/cycling-api.txt
create mode 100644 doc/cyclingspeed-api.txt

diff --git a/doc/cycling-api.txt b/doc/cycling-api.txt
deleted file mode 100644
index 08e11c8..0000000
--- a/doc/cycling-api.txt
+++ /dev/null
@@ -1,100 +0,0 @@
-Cycling Speed and Cadence API description
-*****************************************
-
-Copyright (C) 2012 Tieto Poland
-
-Cycling Speed and Cadence Manager hierarchy
-===========================================
-
-Service org.bluez
-Interface org.bluez.CyclingSpeedManager
-Object path [variable prefix]/{hci0,hci1,...}
-
-Methods RegisterWatcher(object agent)
-
- Registers a watcher to monitor cycling speed and
- cadence measurements.
-
- Possible Errors: org.bluez.Error.InvalidArguments
-
- UnregisterWatcher(object agent)
-
- Unregisters a watcher.
-
-Cycling Speed and Cadence Profile hierarchy
-===========================================
-
-Service org.bluez
-Interface org.bluez.CyclingSpeed
-Object path [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX
-
-Methods SetCumulativeWheelRevolutions(uint32 value)
-
- Sets cumulative wheel revolutions value if
- Cumulative Wheel Revolutions feature is supported.
-
- Possible Errors: org.bluez.Error.NotSupported
-
-Properties string Location (optional) [readwrite]
-
- Current sensor location, if supported.
- If Multiple Sensor Locations feature is supported,
- this property can be set to one of values read from
- SupportedLocations property.
-
- Possible values: "other", "top-of-shoe", "in-shoe",
- "hip", "front-wheel", "left-crank",
- "right-crank", "left-pedal",
- "right-pedal", "front-hub",
- "rear-dropout", "chainstay",
- "rear-wheel", "rear-hub"
-
- array{string} SupportedLocations (optional) [readonly]
-
- List of locations supported by sensor, only present
- if Multiple Sensor Locations feature is supported.
-
- boolean WheelRevolutionDataSupported [readonly]
-
- true if sensor can read and set Cumulative Wheel
- Revolutions value, false otherwise.
-
- boolean MultipleSensorLocationsSupported [readonly]
-
- true if sensor supports Multiple Sensor Locations
- feature and can set Location, false otherwise.
-
-Cycling Speed and Cadence Watcher hierarchy
-===========================================
-
-Service unique name
-Interface org.bluez.CyclingSpeedWatcher
-Object path freely definable
-
-Methods void MeasurementReceived(object device, dict measurement)
-
- This callback is called whenever wheel and/or crank
- revolutions measurement is received from sensor.
-
- Measurement:
-
- uint32 WheelRevolutions (optional):
-
- Cumulative number of wheel revolutions.
-
- uint16 LastWheelEventTime (optional):
-
- Time of last event from wheel sensor.
- Value is expressed in 1/1024 second
- units and can roll over during a ride.
-
- uint16 CrankRevolutions (optional):
-
- Cumulative number of crank revolutions.
- This value can occasionally roll over.
-
- uint16 LastCrankEventTime (optional):
-
- Time of last event from crank sensor.
- Value is expressed in 1/1024 second
- units and can roll over during a ride.
diff --git a/doc/cyclingspeed-api.txt b/doc/cyclingspeed-api.txt
new file mode 100644
index 0000000..08e11c8
--- /dev/null
+++ b/doc/cyclingspeed-api.txt
@@ -0,0 +1,100 @@
+Cycling Speed and Cadence API description
+*****************************************
+
+Copyright (C) 2012 Tieto Poland
+
+Cycling Speed and Cadence Manager hierarchy
+===========================================
+
+Service org.bluez
+Interface org.bluez.CyclingSpeedManager
+Object path [variable prefix]/{hci0,hci1,...}
+
+Methods RegisterWatcher(object agent)
+
+ Registers a watcher to monitor cycling speed and
+ cadence measurements.
+
+ Possible Errors: org.bluez.Error.InvalidArguments
+
+ UnregisterWatcher(object agent)
+
+ Unregisters a watcher.
+
+Cycling Speed and Cadence Profile hierarchy
+===========================================
+
+Service org.bluez
+Interface org.bluez.CyclingSpeed
+Object path [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX
+
+Methods SetCumulativeWheelRevolutions(uint32 value)
+
+ Sets cumulative wheel revolutions value if
+ Cumulative Wheel Revolutions feature is supported.
+
+ Possible Errors: org.bluez.Error.NotSupported
+
+Properties string Location (optional) [readwrite]
+
+ Current sensor location, if supported.
+ If Multiple Sensor Locations feature is supported,
+ this property can be set to one of values read from
+ SupportedLocations property.
+
+ Possible values: "other", "top-of-shoe", "in-shoe",
+ "hip", "front-wheel", "left-crank",
+ "right-crank", "left-pedal",
+ "right-pedal", "front-hub",
+ "rear-dropout", "chainstay",
+ "rear-wheel", "rear-hub"
+
+ array{string} SupportedLocations (optional) [readonly]
+
+ List of locations supported by sensor, only present
+ if Multiple Sensor Locations feature is supported.
+
+ boolean WheelRevolutionDataSupported [readonly]
+
+ true if sensor can read and set Cumulative Wheel
+ Revolutions value, false otherwise.
+
+ boolean MultipleSensorLocationsSupported [readonly]
+
+ true if sensor supports Multiple Sensor Locations
+ feature and can set Location, false otherwise.
+
+Cycling Speed and Cadence Watcher hierarchy
+===========================================
+
+Service unique name
+Interface org.bluez.CyclingSpeedWatcher
+Object path freely definable
+
+Methods void MeasurementReceived(object device, dict measurement)
+
+ This callback is called whenever wheel and/or crank
+ revolutions measurement is received from sensor.
+
+ Measurement:
+
+ uint32 WheelRevolutions (optional):
+
+ Cumulative number of wheel revolutions.
+
+ uint16 LastWheelEventTime (optional):
+
+ Time of last event from wheel sensor.
+ Value is expressed in 1/1024 second
+ units and can roll over during a ride.
+
+ uint16 CrankRevolutions (optional):
+
+ Cumulative number of crank revolutions.
+ This value can occasionally roll over.
+
+ uint16 LastCrankEventTime (optional):
+
+ Time of last event from crank sensor.
+ Value is expressed in 1/1024 second
+ units and can roll over during a ride.
--
1.8.0


2012-12-04 12:43:41

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [PATCH v3 15/20] core: Add CyclingSpeedWatcher interface to default policy

---
src/bluetooth.conf | 1 +
1 file changed, 1 insertion(+)

diff --git a/src/bluetooth.conf b/src/bluetooth.conf
index bd48cf9..4d0d504 100644
--- a/src/bluetooth.conf
+++ b/src/bluetooth.conf
@@ -19,6 +19,7 @@
<allow send_interface="org.bluez.AlertAgent"/>
<allow send_interface="org.bluez.Profile1"/>
<allow send_interface="org.bluez.HeartRateWatcher"/>
+ <allow send_interface="org.bluez.CyclingSpeedWatcher"/>
</policy>

<policy at_console="true">
--
1.8.0


2012-12-04 12:43:40

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [PATCH v3 14/20] cyclingspeed: Add support for Set Cumulative Value

---
profiles/cyclingspeed/cyclingspeed.c | 92 +++++++++++++++++++++++++++++++++++-
1 file changed, 91 insertions(+), 1 deletion(-)

diff --git a/profiles/cyclingspeed/cyclingspeed.c b/profiles/cyclingspeed/cyclingspeed.c
index 58ccf74..8cc7028 100644
--- a/profiles/cyclingspeed/cyclingspeed.c
+++ b/profiles/cyclingspeed/cyclingspeed.c
@@ -76,6 +76,7 @@ struct controlpoint_req {
uint8_t opcode;
guint timeout;
GDBusPendingReply reply_id;
+ DBusMessage *msg;

uint8_t pending_location;
};
@@ -290,6 +291,15 @@ static gboolean controlpoint_timeout(gpointer user_data)
g_dbus_pending_property_error(req->reply_id,
ERROR_INTERFACE ".Failed",
"Operation failed (timeout)");
+ } else if (req->opcode == SET_CUMULATIVE_VALUE) {
+ DBusMessage *reply;
+
+ reply = btd_error_failed(req->msg,
+ "Operation failed (timeout)");
+
+ g_dbus_send_message(btd_get_dbus_connection(), reply);
+
+ dbus_message_unref(req->msg);
}

req->csc->pending_req = NULL;
@@ -310,6 +320,15 @@ static void controlpoint_write_cb(guint8 status, const guint8 *pdu, guint16 len,
g_dbus_pending_property_error(req->reply_id,
ERROR_INTERFACE ".Failed",
"Operation failed (%d)", status);
+ } else if (req->opcode == SET_CUMULATIVE_VALUE) {
+ DBusMessage *reply;
+
+ reply = btd_error_failed(req->msg,
+ "Operation failed");
+
+ g_dbus_send_message(btd_get_dbus_connection(), reply);
+
+ dbus_message_unref(req->msg);
}

req->csc->pending_req = NULL;
@@ -624,6 +643,34 @@ static void controlpoint_property_reply(struct controlpoint_req *req,
}
}

+static void controlpoint_method_reply(struct controlpoint_req *req,
+ uint8_t code)
+{
+ DBusMessage *reply;
+
+ switch (code) {
+ case RSP_SUCCESS:
+ reply = dbus_message_new_method_return(req->msg);
+ break;
+ case RSP_NOT_SUPPORTED:
+ reply = btd_error_not_supported(req->msg);
+ break;
+ case RSP_INVALID_PARAM:
+ reply = btd_error_invalid_args(req->msg);
+ break;
+ case RSP_FAILED:
+ reply = btd_error_failed(req->msg, "Failed");
+ break;
+ default:
+ reply = btd_error_failed(req->msg, "Unknown error");
+ break;
+ }
+
+ g_dbus_send_message(btd_get_dbus_connection(), reply);
+
+ dbus_message_unref(req->msg);
+}
+
static void controlpoint_ind_handler(const uint8_t *pdu, uint16_t len,
gpointer user_data)
{
@@ -675,6 +722,10 @@ static void controlpoint_ind_handler(const uint8_t *pdu, uint16_t len,
}

switch (req->opcode) {
+ case SET_CUMULATIVE_VALUE:
+ controlpoint_method_reply(req, rsp_code);
+ break;
+
case REQUEST_SUPPORTED_SENSOR_LOC:
if (rsp_code == RSP_SUCCESS) {
csc->num_locations = len;
@@ -1083,6 +1134,44 @@ static const GDBusPropertyTable cyclingspeed_device_properties[] = {
{ }
};

+static DBusMessage *set_cumulative_wheel_rev(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct csc *csc = data;
+ dbus_uint32_t value;
+ struct controlpoint_req *req;
+ uint8_t att_val[5]; /* uint8 opcode + uint32 value */
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_UINT32, &value,
+ DBUS_TYPE_INVALID))
+ return btd_error_invalid_args(msg);
+
+ if (csc->pending_req != NULL)
+ return btd_error_in_progress(msg);
+
+ req = g_new(struct controlpoint_req, 1);
+ req->csc = csc;
+ req->opcode = SET_CUMULATIVE_VALUE;
+ req->msg = dbus_message_ref(msg);
+
+ csc->pending_req = req;
+
+ att_val[0] = SET_CUMULATIVE_VALUE;
+ att_put_u32(value, att_val + 1);
+
+ gatt_write_char(csc->attrib, csc->controlpoint_val_handle, att_val,
+ sizeof(att_val), controlpoint_write_cb, req);
+
+ return NULL;
+}
+
+static const GDBusMethodTable cyclingspeed_device_methods[] = {
+ { GDBUS_ASYNC_METHOD("SetCumulativeWheelRevolutions",
+ GDBUS_ARGS({ "value", "u" }), NULL,
+ set_cumulative_wheel_rev) },
+ { }
+};
+
static gint cmp_primary_uuid(gconstpointer a, gconstpointer b)
{
const struct gatt_primary *prim = a;
@@ -1122,7 +1211,8 @@ static int csc_device_probe(struct btd_profile *p,
if (!g_dbus_register_interface(btd_get_dbus_connection(),
device_get_path(device),
CYCLINGSPEED_INTERFACE,
- NULL, NULL,
+ cyclingspeed_device_methods,
+ NULL,
cyclingspeed_device_properties,
csc, destroy_csc)) {
error("D-Bus failed to register %s interface",
--
1.8.0


2012-12-04 12:43:42

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [PATCH v3 16/20] doc: Remove Get-/SetProperties from CSC API document

cyclingspeed plugin uses DBus.Properties instead of custom methods.
---
doc/cycling-api.txt | 20 +-------------------
1 file changed, 1 insertion(+), 19 deletions(-)

diff --git a/doc/cycling-api.txt b/doc/cycling-api.txt
index adbcd33..08e11c8 100644
--- a/doc/cycling-api.txt
+++ b/doc/cycling-api.txt
@@ -28,31 +28,13 @@ Service org.bluez
Interface org.bluez.CyclingSpeed
Object path [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX

-Methods void SetProperty(string name, variant value)
-
- Changes the value of the specified property. Only
- read-write properties can be changed. On success
- this will emit a PropertyChanged signal.
-
- Possible Errors: org.bluez.Error.InvalidArguments
-
- dict GetProperties()
-
- Returns all properties for the interface. See the
- Properties section for the available properties.
-
- SetCumulativeWheelRevolutions(uint32 value)
+Methods SetCumulativeWheelRevolutions(uint32 value)

Sets cumulative wheel revolutions value if
Cumulative Wheel Revolutions feature is supported.

Possible Errors: org.bluez.Error.NotSupported

-Signals PropertyChanged(string name, variant value)
-
- This signal indicates a changed value of the given
- property.
-
Properties string Location (optional) [readwrite]

Current sensor location, if supported.
--
1.8.0


2012-12-04 12:43:37

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [PATCH v3 11/20] cyclingspeed: Add stub to use SC Control Point

This patch implements common functions to use SC Control Point.
Individual procedures will be implemented in subsequent patches.
---
profiles/cyclingspeed/cyclingspeed.c | 145 +++++++++++++++++++++++++++++++++--
1 file changed, 140 insertions(+), 5 deletions(-)

diff --git a/profiles/cyclingspeed/cyclingspeed.c b/profiles/cyclingspeed/cyclingspeed.c
index eace558..866fac8 100644
--- a/profiles/cyclingspeed/cyclingspeed.c
+++ b/profiles/cyclingspeed/cyclingspeed.c
@@ -42,6 +42,11 @@
#include "attio.h"
#include "log.h"

+/* min length for ATT indication or notification: opcode (1b) + handle (2b) */
+#define ATT_HDR_LEN 3
+
+#define ATT_TIMEOUT 30
+
#define CYCLINGSPEED_INTERFACE "org.bluez.CyclingSpeed"
#define CYCLINGSPEED_MANAGER_INTERFACE "org.bluez.CyclingSpeedManager"
#define CYCLINGSPEED_WATCHER_INTERFACE "org.bluez.CyclingSpeedWatcher"
@@ -53,6 +58,25 @@
#define WHEEL_REV_PRESENT 0x01
#define CRANK_REV_PRESENT 0x02

+#define SET_CUMULATIVE_VALUE 0x01
+#define START_SENSOR_CALIBRATION 0x02
+#define UPDATE_SENSOR_LOC 0x03
+#define REQUEST_SUPPORTED_SENSOR_LOC 0x04
+#define RESPONSE_CODE 0x10
+
+#define RSP_SUCCESS 0x01
+#define RSP_NOT_SUPPORTED 0x02
+#define RSP_INVALID_PARAM 0x03
+#define RSP_FAILED 0x04
+
+struct csc;
+
+struct controlpoint_req {
+ struct csc *csc;
+ uint8_t opcode;
+ guint timeout;
+};
+
struct csc_adapter {
struct btd_adapter *adapter;
GSList *devices; /* list of registered devices */
@@ -67,6 +91,8 @@ struct csc {
guint attioid;
/* attio id for measurement characteristics value notifications */
guint attio_measurement_id;
+ /* attio id for SC Control Point characteristics value indications */
+ guint attio_controlpoint_id;

struct att_range *svc_range;

@@ -76,6 +102,8 @@ struct csc {
uint16_t feature;
gboolean has_location;
uint8_t location;
+
+ struct controlpoint_req *pending_req;
};

struct watcher {
@@ -217,6 +245,7 @@ static void destroy_csc(gpointer user_data)

if (csc->attrib != NULL) {
g_attrib_unregister(csc->attrib, csc->attio_measurement_id);
+ g_attrib_unregister(csc->attrib, csc->attio_controlpoint_id);
g_attrib_unref(csc->attrib);
}

@@ -236,6 +265,35 @@ static void char_write_cb(guint8 status, const guint8 *pdu, guint16 len,
g_free(msg);
}

+static gboolean controlpoint_timeout(gpointer user_data)
+{
+ struct controlpoint_req *req = user_data;
+
+ req->csc->pending_req = NULL;
+ g_free(req);
+
+ return FALSE;
+}
+
+__attribute__((unused)) /* TODO: remove once controlpoint ops are implemented */
+static void controlpoint_write_cb(guint8 status, const guint8 *pdu, guint16 len,
+ gpointer user_data)
+{
+ struct controlpoint_req *req = user_data;
+
+ if (status != 0) {
+ error("SC Control Point write failed (opcode=%d)", req->opcode);
+
+ req->csc->pending_req = NULL;
+ g_free(req);
+
+ return;
+ }
+
+ req->timeout = g_timeout_add_seconds(ATT_TIMEOUT, controlpoint_timeout,
+ req);
+}
+
static void read_feature_cb(guint8 status, const guint8 *pdu,
guint16 len, gpointer user_data)
{
@@ -317,6 +375,8 @@ static void discover_desc_cb(guint8 status, const guint8 *pdu,
for (i = 0; i < list->num; i++) {
uint8_t *value;
uint16_t handle, uuid;
+ uint8_t attr_val[2];
+ char *msg;

value = list->data[i];
handle = att_get_u16(value);
@@ -326,9 +386,6 @@ static void discover_desc_cb(guint8 status, const guint8 *pdu,
continue;

if (g_strcmp0(ch->uuid, CSC_MEASUREMENT_UUID) == 0) {
- char *msg;
- uint8_t attr_val[2];
-
ch->csc->measurement_ccc_handle = handle;

if (g_slist_length(ch->csc->cadapter->watchers) == 0) {
@@ -340,10 +397,16 @@ static void discover_desc_cb(guint8 status, const guint8 *pdu,
msg = g_strdup("Enable measurement");
}

- gatt_write_char(ch->csc->attrib, handle, attr_val,
- sizeof(attr_val), char_write_cb, msg);
+ } else if (g_strcmp0(ch->uuid, SC_CONTROL_POINT_UUID) == 0) {
+ att_put_u16(GATT_CLIENT_CHARAC_CFG_IND_BIT, attr_val);
+ msg = g_strdup("Enable SC Control Point indications");
+ } else {
+ break;
}

+ gatt_write_char(ch->csc->attrib, handle, attr_val,
+ sizeof(attr_val), char_write_cb, msg);
+
/* We only want CCC, can break here */
break;
}
@@ -481,6 +544,67 @@ static void measurement_notify_handler(const uint8_t *pdu, uint16_t len,
process_measurement(csc, pdu + 3, len - 3);
}

+static void controlpoint_ind_handler(const uint8_t *pdu, uint16_t len,
+ gpointer user_data)
+{
+ struct csc *csc = user_data;
+ struct controlpoint_req *req = csc->pending_req;
+ uint8_t opcode;
+ uint8_t req_opcode;
+ uint8_t *opdu;
+ uint16_t olen;
+ size_t plen;
+
+ if (len < ATT_HDR_LEN) {
+ error("Invalid PDU received");
+ return;
+ }
+
+ /* skip ATT header */
+ pdu += ATT_HDR_LEN;
+ len -= ATT_HDR_LEN;
+
+ if (len < 1) {
+ error("Op Code missing");
+ goto done;
+ }
+
+ opcode = *pdu;
+ pdu++;
+ len--;
+
+ if (opcode != RESPONSE_CODE) {
+ DBG("Unsupported Op Code received (%d)", opcode);
+ goto done;
+ }
+
+ if (len < 2) {
+ error("Invalid Response Code PDU received");
+ goto done;
+ }
+
+ req_opcode = *pdu;
+ /* skip response code for now */
+ pdu += 2;
+ len -= 2;
+
+ if (req == NULL || req->opcode != req_opcode) {
+ DBG("Indication received without pending request");
+ goto done;
+ }
+
+ /* TODO: handle response */
+
+ csc->pending_req = NULL;
+ g_source_remove(req->timeout);
+ g_free(req);
+
+done:
+ opdu = g_attrib_get_buffer(csc->attrib, &plen);
+ olen = enc_confirmation(opdu, plen);
+ if (olen > 0)
+ g_attrib_send(csc->attrib, 0, opdu, olen, NULL, NULL, NULL);
+}

static void discover_char_cb(GSList *chars, guint8 status, gpointer user_data)
{
@@ -514,6 +638,12 @@ static void discover_char_cb(GSList *chars, guint8 status, gpointer user_data)
} else if (g_strcmp0(c->uuid, SC_CONTROL_POINT_UUID) == 0) {
DBG("SC Control Point supported");
csc->controlpoint_val_handle = c->value_handle;
+
+ csc->attio_controlpoint_id = g_attrib_register(
+ csc->attrib, ATT_OP_HANDLE_IND,
+ c->value_handle,
+ controlpoint_ind_handler, csc, NULL);
+
discover_desc(csc, c, c_next);
}
}
@@ -581,6 +711,11 @@ static void attio_disconnected_cb(gpointer user_data)
csc->attio_measurement_id = 0;
}

+ if (csc->attio_controlpoint_id > 0) {
+ g_attrib_unregister(csc->attrib, csc->attio_controlpoint_id);
+ csc->attio_controlpoint_id = 0;
+ }
+
g_attrib_unref(csc->attrib);
csc->attrib = NULL;
}
--
1.8.0


2012-12-04 12:43:38

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [PATCH v3 12/20] cyclingspeed: Add support for Request Supported Sensor Locations

---
profiles/cyclingspeed/cyclingspeed.c | 61 +++++++++++++++++++++++++++++++++---
1 file changed, 57 insertions(+), 4 deletions(-)

diff --git a/profiles/cyclingspeed/cyclingspeed.c b/profiles/cyclingspeed/cyclingspeed.c
index 866fac8..8283894 100644
--- a/profiles/cyclingspeed/cyclingspeed.c
+++ b/profiles/cyclingspeed/cyclingspeed.c
@@ -102,6 +102,8 @@ struct csc {
uint16_t feature;
gboolean has_location;
uint8_t location;
+ uint8_t num_locations;
+ uint8_t *locations;

struct controlpoint_req *pending_req;
};
@@ -251,6 +253,7 @@ static void destroy_csc(gpointer user_data)

btd_device_unref(csc->dev);
g_free(csc->svc_range);
+ g_free(csc->locations);
g_free(csc);
}

@@ -275,7 +278,6 @@ static gboolean controlpoint_timeout(gpointer user_data)
return FALSE;
}

-__attribute__((unused)) /* TODO: remove once controlpoint ops are implemented */
static void controlpoint_write_cb(guint8 status, const guint8 *pdu, guint16 len,
gpointer user_data)
{
@@ -294,6 +296,20 @@ static void controlpoint_write_cb(guint8 status, const guint8 *pdu, guint16 len,
req);
}

+static void read_supported_locations(struct csc *csc)
+{
+ struct controlpoint_req *req;
+
+ req = g_new0(struct controlpoint_req, 1);
+ req->csc = csc;
+ req->opcode = REQUEST_SUPPORTED_SENSOR_LOC;
+
+ csc->pending_req = req;
+
+ gatt_write_char(csc->attrib, csc->controlpoint_val_handle, &req->opcode,
+ sizeof(req->opcode), controlpoint_write_cb, req);
+}
+
static void read_feature_cb(guint8 status, const guint8 *pdu,
guint16 len, gpointer user_data)
{
@@ -318,6 +334,10 @@ static void read_feature_cb(guint8 status, const guint8 *pdu,
}

csc->feature = att_get_u16(value);
+
+ if ((csc->feature & MULTI_SENSOR_LOC_SUPPORT)
+ && (csc->locations == NULL))
+ read_supported_locations(csc);
}

static void read_location_cb(guint8 status, const guint8 *pdu,
@@ -551,6 +571,7 @@ static void controlpoint_ind_handler(const uint8_t *pdu, uint16_t len,
struct controlpoint_req *req = csc->pending_req;
uint8_t opcode;
uint8_t req_opcode;
+ uint8_t rsp_code;
uint8_t *opdu;
uint16_t olen;
size_t plen;
@@ -584,7 +605,7 @@ static void controlpoint_ind_handler(const uint8_t *pdu, uint16_t len,
}

req_opcode = *pdu;
- /* skip response code for now */
+ rsp_code = *(pdu + 1);
pdu += 2;
len -= 2;

@@ -593,7 +614,16 @@ static void controlpoint_ind_handler(const uint8_t *pdu, uint16_t len,
goto done;
}

- /* TODO: handle response */
+ switch (req->opcode) {
+ case REQUEST_SUPPORTED_SENSOR_LOC:
+ if (rsp_code == RSP_SUCCESS) {
+ csc->num_locations = len;
+ csc->locations = g_memdup(pdu, len);
+ } else {
+ error("Failed to read Supported Sendor Locations");
+ }
+ break;
+ }

csc->pending_req = NULL;
g_source_remove(req->timeout);
@@ -868,6 +898,29 @@ static gboolean property_exists_location(const GDBusPropertyTable *property,
return csc->has_location;
}

+static gboolean property_get_locations(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct csc *csc = data;
+ DBusMessageIter entry;
+ int i;
+
+ if (!(csc->feature & MULTI_SENSOR_LOC_SUPPORT))
+ return FALSE;
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+ DBUS_TYPE_STRING_AS_STRING, &entry);
+ for (i = 0; i < csc->num_locations; i++) {
+ char *loc = g_strdup(location2str(csc->locations[i]));
+ dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &loc);
+ g_free(loc);
+ }
+
+ dbus_message_iter_close_container(iter, &entry);
+
+ return TRUE;
+}
+
static gboolean property_exists_locations(const GDBusPropertyTable *property,
void *data)
{
@@ -903,7 +956,7 @@ static gboolean property_get_multi_loc_sup(const GDBusPropertyTable *property,
static const GDBusPropertyTable cyclingspeed_device_properties[] = {
{ "Location", "s", property_get_location, NULL,
property_exists_location },
- { "SupportedLocations", "as", NULL, NULL,
+ { "SupportedLocations", "as", property_get_locations, NULL,
property_exists_locations },
{ "WheelRevolutionDataSupported", "b", property_get_wheel_rev_sup },
{ "MultipleLocationsSupported", "b", property_get_multi_loc_sup },
--
1.8.0


2012-12-04 12:43:33

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [PATCH v3 07/20] cyclingspeed: Add CyclingSpeedManager interface

This patch registers org.bluez.CyclingSpeedManager interface for each
adapter to allow registration and deregistration of measurement watchers.
---
profiles/cyclingspeed/cyclingspeed.c | 151 ++++++++++++++++++++++++++++++++++-
1 file changed, 150 insertions(+), 1 deletion(-)

diff --git a/profiles/cyclingspeed/cyclingspeed.c b/profiles/cyclingspeed/cyclingspeed.c
index ec52ab4..cf58a22 100644
--- a/profiles/cyclingspeed/cyclingspeed.c
+++ b/profiles/cyclingspeed/cyclingspeed.c
@@ -35,6 +35,7 @@
#include "device.h"
#include "profile.h"
#include "dbus-common.h"
+#include "error.h"
#include "attrib/gattrib.h"
#include "attrib/att.h"
#include "attrib/gatt.h"
@@ -42,10 +43,12 @@
#include "log.h"

#define CYCLINGSPEED_INTERFACE "org.bluez.CyclingSpeed"
+#define CYCLINGSPEED_MANAGER_INTERFACE "org.bluez.CyclingSpeedManager"

struct csc_adapter {
struct btd_adapter *adapter;
GSList *devices; /* list of registered devices */
+ GSList *watchers;
};

struct csc {
@@ -65,6 +68,13 @@ struct csc {
uint8_t location;
};

+struct watcher {
+ struct csc_adapter *cadapter;
+ guint id;
+ char *srv;
+ char *path;
+};
+
struct characteristic {
struct csc *csc;
char uuid[MAX_LEN_UUID_STR + 1];
@@ -94,6 +104,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 csc_adapter *find_csc_adapter(struct btd_adapter *adapter)
{
GSList *l = g_slist_find_custom(csc_adapters, adapter, cmp_adapter);
@@ -104,10 +127,47 @@ static struct csc_adapter *find_csc_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 remove_watcher(gpointer user_data)
+{
+ struct watcher *watcher = user_data;
+
+ g_dbus_remove_watch(btd_get_dbus_connection(), watcher->id);
+}
+
static void destroy_csc_adapter(gpointer user_data)
{
struct csc_adapter *cadapter = user_data;

+ g_slist_free_full(cadapter->watchers, remove_watcher);
+
g_free(cadapter);
}

@@ -312,6 +372,81 @@ static void attio_disconnected_cb(gpointer user_data)
csc->attrib = NULL;
}

+static void watcher_exit_cb(DBusConnection *conn, void *user_data)
+{
+ struct watcher *watcher = user_data;
+ struct csc_adapter *cadapter = watcher->cadapter;
+
+ DBG("cycling watcher [%s] disconnected", watcher->path);
+
+ cadapter->watchers = g_slist_remove(cadapter->watchers, watcher);
+ g_dbus_remove_watch(conn, watcher->id);
+}
+
+static DBusMessage *register_watcher(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct csc_adapter *cadapter = 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(cadapter->watchers, sender, path);
+ if (watcher != NULL)
+ return btd_error_already_exists(msg);
+
+ watcher = g_new0(struct watcher, 1);
+ watcher->cadapter = cadapter;
+ 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);
+
+ cadapter->watchers = g_slist_prepend(cadapter->watchers, watcher);
+
+ DBG("cycling watcher [%s] registered", path);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *unregister_watcher(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct csc_adapter *cadapter = 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(cadapter->watchers, sender, path);
+ if (watcher == NULL)
+ return btd_error_does_not_exist(msg);
+
+ cadapter->watchers = g_slist_remove(cadapter->watchers, watcher);
+ g_dbus_remove_watch(conn, watcher->id);
+
+ DBG("cycling watcher [%s] unregistered", path);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static const GDBusMethodTable cyclingspeed_manager_methods[] = {
+ { GDBUS_METHOD("RegisterWatcher",
+ GDBUS_ARGS({ "agent", "o" }), NULL,
+ register_watcher) },
+ { GDBUS_METHOD("UnregisterWatcher",
+ GDBUS_ARGS({ "agent", "o" }), NULL,
+ unregister_watcher) },
+ { }
+};
+
static int csc_adapter_probe(struct btd_profile *p, struct btd_adapter *adapter)
{
struct csc_adapter *cadapter;
@@ -321,6 +456,18 @@ static int csc_adapter_probe(struct btd_profile *p, struct btd_adapter *adapter)

csc_adapters = g_slist_prepend(csc_adapters, cadapter);

+ if (!g_dbus_register_interface(btd_get_dbus_connection(),
+ adapter_get_path(adapter),
+ CYCLINGSPEED_MANAGER_INTERFACE,
+ cyclingspeed_manager_methods,
+ NULL, NULL, cadapter,
+ destroy_csc_adapter)) {
+ error("D-Bus failed to register %s interface",
+ CYCLINGSPEED_MANAGER_INTERFACE);
+ destroy_csc_adapter(cadapter);
+ return -EIO;
+ }
+
return 0;
}

@@ -335,7 +482,9 @@ static void csc_adapter_remove(struct btd_profile *p,

csc_adapters = g_slist_remove(csc_adapters, cadapter);

- destroy_csc_adapter(cadapter);
+ g_dbus_unregister_interface(btd_get_dbus_connection(),
+ adapter_get_path(cadapter->adapter),
+ CYCLINGSPEED_MANAGER_INTERFACE);
}

static gint cmp_primary_uuid(gconstpointer a, gconstpointer b)
--
1.8.0


2012-12-04 12:43:34

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [PATCH v3 08/20] cyclingspeed: Add support to enable measurement notifications

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

diff --git a/profiles/cyclingspeed/cyclingspeed.c b/profiles/cyclingspeed/cyclingspeed.c
index cf58a22..5cb4ffd 100644
--- a/profiles/cyclingspeed/cyclingspeed.c
+++ b/profiles/cyclingspeed/cyclingspeed.c
@@ -186,6 +186,17 @@ static void destroy_csc(gpointer user_data)
g_free(csc);
}

+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 read_feature_cb(guint8 status, const guint8 *pdu,
guint16 len, gpointer user_data)
{
@@ -275,9 +286,25 @@ static void discover_desc_cb(guint8 status, const guint8 *pdu,
if (uuid != GATT_CLIENT_CHARAC_CFG_UUID)
continue;

- if (g_strcmp0(ch->uuid, CSC_MEASUREMENT_UUID) == 0)
+ if (g_strcmp0(ch->uuid, CSC_MEASUREMENT_UUID) == 0) {
+ char *msg;
+ uint8_t attr_val[2];
+
ch->csc->measurement_ccc_handle = handle;

+ if (g_slist_length(ch->csc->cadapter->watchers) == 0) {
+ att_put_u16(0x0000, attr_val);
+ msg = g_strdup("Disable measurement");
+ } else {
+ att_put_u16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT,
+ attr_val);
+ msg = g_strdup("Enable measurement");
+ }
+
+ gatt_write_char(ch->csc->attrib, handle, attr_val,
+ sizeof(attr_val), char_write_cb, msg);
+ }
+
/* We only want CCC, can break here */
break;
}
@@ -349,6 +376,40 @@ static void discover_char_cb(GSList *chars, guint8 status, gpointer user_data)
read_feature_cb, csc);
}

+static void enable_measurement(gpointer data, gpointer user_data)
+{
+ struct csc *csc = data;
+ uint16_t handle = csc->measurement_ccc_handle;
+ uint8_t value[2];
+ char *msg;
+
+ if (csc->attrib == NULL || !handle)
+ return;
+
+ att_put_u16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, value);
+ msg = g_strdup("Enable measurement");
+
+ gatt_write_char(csc->attrib, handle, value, sizeof(value),
+ char_write_cb, msg);
+}
+
+static void disable_measurement(gpointer data, gpointer user_data)
+{
+ struct csc *csc = data;
+ uint16_t handle = csc->measurement_ccc_handle;
+ uint8_t value[2];
+ char *msg;
+
+ if (csc->attrib == NULL || !handle)
+ return;
+
+ att_put_u16(0x0000, value);
+ msg = g_strdup("Disable measurement");
+
+ gatt_write_char(csc->attrib, handle, value, sizeof(value),
+ char_write_cb, msg);
+}
+
static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
{
struct csc *csc = user_data;
@@ -381,6 +442,9 @@ static void watcher_exit_cb(DBusConnection *conn, void *user_data)

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

static DBusMessage *register_watcher(DBusConnection *conn, DBusMessage *msg,
@@ -406,6 +470,9 @@ static DBusMessage *register_watcher(DBusConnection *conn, DBusMessage *msg,
watcher->srv = g_strdup(sender);
watcher->path = g_strdup(path);

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

DBG("cycling watcher [%s] registered", path);
@@ -432,6 +499,9 @@ static DBusMessage *unregister_watcher(DBusConnection *conn, DBusMessage *msg,
cadapter->watchers = g_slist_remove(cadapter->watchers, watcher);
g_dbus_remove_watch(conn, watcher->id);

+ if (g_slist_length(cadapter->watchers) == 0)
+ g_slist_foreach(cadapter->devices, disable_measurement, 0);
+
DBG("cycling watcher [%s] unregistered", path);

return dbus_message_new_method_return(msg);
--
1.8.0


2012-12-04 12:43:35

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [PATCH v3 09/20] cyclingspeed: Process measurement notifications

---
profiles/cyclingspeed/cyclingspeed.c | 139 ++++++++++++++++++++++++++++++++++-
1 file changed, 138 insertions(+), 1 deletion(-)

diff --git a/profiles/cyclingspeed/cyclingspeed.c b/profiles/cyclingspeed/cyclingspeed.c
index 5cb4ffd..a3e2d61 100644
--- a/profiles/cyclingspeed/cyclingspeed.c
+++ b/profiles/cyclingspeed/cyclingspeed.c
@@ -44,6 +44,14 @@

#define CYCLINGSPEED_INTERFACE "org.bluez.CyclingSpeed"
#define CYCLINGSPEED_MANAGER_INTERFACE "org.bluez.CyclingSpeedManager"
+#define CYCLINGSPEED_WATCHER_INTERFACE "org.bluez.CyclingSpeedWatcher"
+
+#define WHEEL_REV_SUPPORT 0x01
+#define CRANK_REV_SUPPORT 0x02
+#define MULTI_SENSOR_LOC_SUPPORT 0x04
+
+#define WHEEL_REV_PRESENT 0x01
+#define CRANK_REV_PRESENT 0x02

struct csc_adapter {
struct btd_adapter *adapter;
@@ -57,6 +65,8 @@ struct csc {

GAttrib *attrib;
guint attioid;
+ /* attio id for measurement characteristics value notifications */
+ guint attio_measurement_id;

struct att_range *svc_range;

@@ -75,6 +85,18 @@ struct watcher {
char *path;
};

+struct measurement {
+ struct csc *csc;
+
+ bool has_wheel_rev;
+ uint32_t wheel_rev;
+ uint16_t last_wheel_time;
+
+ bool has_crank_rev;
+ uint16_t crank_rev;
+ uint16_t last_crank_time;
+};
+
struct characteristic {
struct csc *csc;
char uuid[MAX_LEN_UUID_STR + 1];
@@ -178,8 +200,10 @@ static void destroy_csc(gpointer user_data)
if (csc->attioid > 0)
btd_device_remove_attio_callback(csc->dev, csc->attioid);

- if (csc->attrib != NULL)
+ if (csc->attrib != NULL) {
+ g_attrib_unregister(csc->attrib, csc->attio_measurement_id);
g_attrib_unref(csc->attrib);
+ }

btd_device_unref(csc->dev);
g_free(csc->svc_range);
@@ -340,6 +364,109 @@ static void discover_desc(struct csc *csc, struct gatt_char *c,
gatt_find_info(csc->attrib, start, end, discover_desc_cb, ch);
}

+static void update_watcher(gpointer data, gpointer user_data)
+{
+ struct watcher *w = data;
+ struct measurement *m = user_data;
+ struct csc *csc = m->csc;
+ const gchar *path = device_get_path(csc->dev);
+ DBusMessageIter iter;
+ DBusMessageIter dict;
+ DBusMessage *msg;
+
+ msg = dbus_message_new_method_call(w->srv, w->path,
+ CYCLINGSPEED_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);
+
+ if (m->has_wheel_rev) {
+ dict_append_entry(&dict, "WheelRevolutions",
+ DBUS_TYPE_UINT32, &m->wheel_rev);
+ dict_append_entry(&dict, "LastWheelEventTime",
+ DBUS_TYPE_UINT16, &m->last_wheel_time);
+ }
+
+ if (m->has_crank_rev) {
+ dict_append_entry(&dict, "CrankRevolutions",
+ DBUS_TYPE_UINT16, &m->crank_rev);
+ dict_append_entry(&dict, "LastCrankEventTime",
+ DBUS_TYPE_UINT16, &m->last_crank_time);
+ }
+
+ 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 csc *csc, const uint8_t *pdu,
+ uint16_t len)
+{
+ struct measurement m;
+ uint8_t flags;
+
+ flags = *pdu;
+
+ pdu++;
+ len--;
+
+ memset(&m, 0, sizeof(m));
+
+ if ((flags & WHEEL_REV_PRESENT) && (csc->feature & WHEEL_REV_SUPPORT)) {
+ if (len < 6) {
+ error("Wheel revolutions data fields missing");
+ return;
+ }
+
+ m.has_wheel_rev = true;
+ m.wheel_rev = att_get_u32(pdu);
+ m.last_wheel_time = att_get_u16(pdu + 4);
+ pdu += 6;
+ len -= 6;
+ }
+
+ if ((flags & CRANK_REV_PRESENT) && (csc->feature & CRANK_REV_SUPPORT)) {
+ if (len < 4) {
+ error("Crank revolutions data fields missing");
+ return;
+ }
+
+ m.has_crank_rev = true;
+ m.crank_rev = att_get_u16(pdu);
+ m.last_crank_time = att_get_u16(pdu + 2);
+ pdu += 4;
+ len -= 4;
+ }
+
+ /* Notify all registered watchers */
+ m.csc = csc;
+ g_slist_foreach(csc->cadapter->watchers, update_watcher, &m);
+}
+
+static void measurement_notify_handler(const uint8_t *pdu, uint16_t len,
+ gpointer user_data)
+{
+ struct csc *csc = user_data;
+
+ /* should be at least opcode (1b) + handle (2b) */
+ if (len < 3) {
+ error("Invalid PDU received");
+ return;
+ }
+
+ process_measurement(csc, pdu + 3, len - 3);
+}
+
+
static void discover_char_cb(GSList *chars, guint8 status, gpointer user_data)
{
struct csc *csc = user_data;
@@ -357,6 +484,11 @@ static void discover_char_cb(GSList *chars, guint8 status, gpointer user_data)
(chars->next ? chars->next->data : NULL);

if (g_strcmp0(c->uuid, CSC_MEASUREMENT_UUID) == 0) {
+ csc->attio_measurement_id =
+ g_attrib_register(csc->attrib,
+ ATT_OP_HANDLE_NOTIFY, c->value_handle,
+ measurement_notify_handler, csc, NULL);
+
discover_desc(csc, c, c_next);
} else if (g_strcmp0(c->uuid, CSC_FEATURE_UUID) == 0) {
feature_val_handle = c->value_handle;
@@ -429,6 +561,11 @@ static void attio_disconnected_cb(gpointer user_data)

DBG("");

+ if (csc->attio_measurement_id > 0) {
+ g_attrib_unregister(csc->attrib, csc->attio_measurement_id);
+ csc->attio_measurement_id = 0;
+ }
+
g_attrib_unref(csc->attrib);
csc->attrib = NULL;
}
--
1.8.0


2012-12-04 12:43:32

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [PATCH v3 06/20] cyclingspeed: Read Sensor Location characteristic value

---
profiles/cyclingspeed/cyclingspeed.c | 40 +++++++++++++++++++++++++++++++++++-
1 file changed, 39 insertions(+), 1 deletion(-)

diff --git a/profiles/cyclingspeed/cyclingspeed.c b/profiles/cyclingspeed/cyclingspeed.c
index 7f79116..ec52ab4 100644
--- a/profiles/cyclingspeed/cyclingspeed.c
+++ b/profiles/cyclingspeed/cyclingspeed.c
@@ -24,6 +24,7 @@
#include <config.h>
#endif

+#include <gdbus.h>
#include <errno.h>
#include <stdbool.h>
#include <glib.h>
@@ -33,12 +34,15 @@
#include "adapter.h"
#include "device.h"
#include "profile.h"
+#include "dbus-common.h"
#include "attrib/gattrib.h"
#include "attrib/att.h"
#include "attrib/gatt.h"
#include "attio.h"
#include "log.h"

+#define CYCLINGSPEED_INTERFACE "org.bluez.CyclingSpeed"
+
struct csc_adapter {
struct btd_adapter *adapter;
GSList *devices; /* list of registered devices */
@@ -57,6 +61,8 @@ struct csc {
uint16_t controlpoint_val_handle;

uint16_t feature;
+ gboolean has_location;
+ uint8_t location;
};

struct characteristic {
@@ -146,6 +152,37 @@ static void read_feature_cb(guint8 status, const guint8 *pdu,
csc->feature = att_get_u16(value);
}

+static void read_location_cb(guint8 status, const guint8 *pdu,
+ guint16 len, gpointer user_data)
+{
+ struct csc *csc = user_data;
+ uint8_t value;
+ ssize_t vlen;
+
+ if (status) {
+ error("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 value length for Sensor Location");
+ return;
+ }
+
+ csc->has_location = TRUE;
+ csc->location = value;
+
+ g_dbus_emit_property_changed(btd_get_dbus_connection(),
+ device_get_path(csc->dev),
+ CYCLINGSPEED_INTERFACE, "Location");
+}
+
static void discover_desc_cb(guint8 status, const guint8 *pdu,
guint16 len, gpointer user_data)
{
@@ -238,7 +275,8 @@ static void discover_char_cb(GSList *chars, guint8 status, gpointer user_data)
feature_val_handle = c->value_handle;
} else if (g_strcmp0(c->uuid, SENSOR_LOCATION_UUID) == 0) {
DBG("Sensor Location supported");
- /* TODO: read characterictic value */
+ gatt_read_char(csc->attrib, c->value_handle,
+ read_location_cb, csc);
} else if (g_strcmp0(c->uuid, SC_CONTROL_POINT_UUID) == 0) {
DBG("SC Control Point supported");
csc->controlpoint_val_handle = c->value_handle;
--
1.8.0


2012-12-04 12:43:29

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [PATCH v3 03/20] cyclingspeed: Discover CSCS characteristics

---
lib/uuid.h | 4 +++
profiles/cyclingspeed/cyclingspeed.c | 60 ++++++++++++++++++++++++++++++++++++
2 files changed, 64 insertions(+)

diff --git a/lib/uuid.h b/lib/uuid.h
index a9a3b13..1e8188a 100644
--- a/lib/uuid.h
+++ b/lib/uuid.h
@@ -75,6 +75,10 @@ extern "C" {
#define MEASUREMENT_INTERVAL_UUID "00002a21-0000-1000-8000-00805f9b34fb"

#define CYCLING_SC_UUID "00001816-0000-1000-8000-00805f9b34fb"
+#define CSC_MEASUREMENT_UUID "00002a5b-0000-1000-8000-00805f9b34fb"
+#define CSC_FEATURE_UUID "00002a5c-0000-1000-8000-00805f9b34fb"
+#define SENSOR_LOCATION_UUID "00002a5d-0000-1000-8000-00805f9b34fb"
+#define SC_CONTROL_POINT_UUID "00002a55-0000-1000-8000-00805f9b34fb"

#define RFCOMM_UUID_STR "00000003-0000-1000-8000-00805f9b34fb"

diff --git a/profiles/cyclingspeed/cyclingspeed.c b/profiles/cyclingspeed/cyclingspeed.c
index e0f458d..862383a 100644
--- a/profiles/cyclingspeed/cyclingspeed.c
+++ b/profiles/cyclingspeed/cyclingspeed.c
@@ -50,6 +50,10 @@ struct csc {

GAttrib *attrib;
guint attioid;
+
+ struct att_range *svc_range;
+
+ uint16_t controlpoint_val_handle;
};

static GSList *csc_adapters = NULL;
@@ -104,9 +108,38 @@ static void destroy_csc(gpointer user_data)
g_attrib_unref(csc->attrib);

btd_device_unref(csc->dev);
+ g_free(csc->svc_range);
g_free(csc);
}

+static void discover_char_cb(GSList *chars, guint8 status, gpointer user_data)
+{
+ struct csc *csc = user_data;
+
+ if (status) {
+ error("Discover CSCS characteristics: %s",
+ att_ecode2str(status));
+ return;
+ }
+
+ for (; chars; chars = chars->next) {
+ struct gatt_char *c = chars->data;
+
+ if (g_strcmp0(c->uuid, CSC_MEASUREMENT_UUID) == 0) {
+ /* TODO: discover CCC handle */
+ } else if (g_strcmp0(c->uuid, CSC_FEATURE_UUID) == 0) {
+ /* TODO: read characterictic value */
+ } else if (g_strcmp0(c->uuid, SENSOR_LOCATION_UUID) == 0) {
+ DBG("Sensor Location supported");
+ /* TODO: read characterictic value */
+ } else if (g_strcmp0(c->uuid, SC_CONTROL_POINT_UUID) == 0) {
+ DBG("SC Control Point supported");
+ csc->controlpoint_val_handle = c->value_handle;
+ /* TODO: discover CCC handle */
+ }
+ }
+}
+
static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
{
struct csc *csc = user_data;
@@ -114,6 +147,10 @@ static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
DBG("");

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

static void attio_disconnected_cb(gpointer user_data)
@@ -152,12 +189,31 @@ static void csc_adapter_remove(struct btd_profile *p,
destroy_csc_adapter(cadapter);
}

+static gint cmp_primary_uuid(gconstpointer a, gconstpointer b)
+{
+ const struct gatt_primary *prim = a;
+ const char *uuid = b;
+
+ return g_strcmp0(prim->uuid, uuid);
+}
+
static int csc_device_probe(struct btd_profile *p,
struct btd_device *device, GSList *uuids)
{
struct btd_adapter *adapter;
struct csc_adapter *cadapter;
struct csc *csc;
+ struct gatt_primary *prim;
+ GSList *primaries;
+ GSList *l;
+
+ primaries = btd_device_get_primaries(device);
+
+ l = g_slist_find_custom(primaries, CYCLING_SC_UUID, cmp_primary_uuid);
+ if (l == NULL)
+ return -EINVAL;
+
+ prim = l->data;

adapter = device_get_adapter(device);

@@ -169,6 +225,10 @@ static int csc_device_probe(struct btd_profile *p,
csc->dev = btd_device_ref(device);
csc->cadapter = cadapter;

+ csc->svc_range = g_new0(struct att_range, 1);
+ csc->svc_range->start = prim->range.start;
+ csc->svc_range->end = prim->range.end;
+
cadapter->devices = g_slist_prepend(cadapter->devices, csc);

csc->attioid = btd_device_add_attio_callback(device, attio_connected_cb,
--
1.8.0


2012-12-04 12:43:31

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [PATCH v3 05/20] cyclingspeed: Read CSC Feature characteristic value

---
profiles/cyclingspeed/cyclingspeed.c | 35 ++++++++++++++++++++++++++++++++++-
1 file changed, 34 insertions(+), 1 deletion(-)

diff --git a/profiles/cyclingspeed/cyclingspeed.c b/profiles/cyclingspeed/cyclingspeed.c
index 32a088d..7f79116 100644
--- a/profiles/cyclingspeed/cyclingspeed.c
+++ b/profiles/cyclingspeed/cyclingspeed.c
@@ -55,6 +55,8 @@ struct csc {

uint16_t measurement_ccc_handle;
uint16_t controlpoint_val_handle;
+
+ uint16_t feature;
};

struct characteristic {
@@ -118,6 +120,32 @@ static void destroy_csc(gpointer user_data)
g_free(csc);
}

+static void read_feature_cb(guint8 status, const guint8 *pdu,
+ guint16 len, gpointer user_data)
+{
+ struct csc *csc = user_data;
+ uint8_t value[2];
+ ssize_t vlen;
+
+ if (status) {
+ error("CSC Feature 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 value length for CSC Feature");
+ return;
+ }
+
+ csc->feature = att_get_u16(value);
+}
+
static void discover_desc_cb(guint8 status, const guint8 *pdu,
guint16 len, gpointer user_data)
{
@@ -191,6 +219,7 @@ static void discover_desc(struct csc *csc, struct gatt_char *c,
static void discover_char_cb(GSList *chars, guint8 status, gpointer user_data)
{
struct csc *csc = user_data;
+ uint16_t feature_val_handle = 0;

if (status) {
error("Discover CSCS characteristics: %s",
@@ -206,7 +235,7 @@ static void discover_char_cb(GSList *chars, guint8 status, gpointer user_data)
if (g_strcmp0(c->uuid, CSC_MEASUREMENT_UUID) == 0) {
discover_desc(csc, c, c_next);
} else if (g_strcmp0(c->uuid, CSC_FEATURE_UUID) == 0) {
- /* TODO: read characterictic value */
+ feature_val_handle = c->value_handle;
} else if (g_strcmp0(c->uuid, SENSOR_LOCATION_UUID) == 0) {
DBG("Sensor Location supported");
/* TODO: read characterictic value */
@@ -216,6 +245,10 @@ static void discover_char_cb(GSList *chars, guint8 status, gpointer user_data)
discover_desc(csc, c, c_next);
}
}
+
+ if (feature_val_handle > 0)
+ gatt_read_char(csc->attrib, feature_val_handle,
+ read_feature_cb, csc);
}

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


2012-12-04 12:43:30

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [PATCH v3 04/20] cyclingspeed: Discover characteristics CCC

---
profiles/cyclingspeed/cyclingspeed.c | 82 +++++++++++++++++++++++++++++++++++-
1 file changed, 80 insertions(+), 2 deletions(-)

diff --git a/profiles/cyclingspeed/cyclingspeed.c b/profiles/cyclingspeed/cyclingspeed.c
index 862383a..32a088d 100644
--- a/profiles/cyclingspeed/cyclingspeed.c
+++ b/profiles/cyclingspeed/cyclingspeed.c
@@ -53,9 +53,15 @@ struct csc {

struct att_range *svc_range;

+ uint16_t measurement_ccc_handle;
uint16_t controlpoint_val_handle;
};

+struct characteristic {
+ struct csc *csc;
+ char uuid[MAX_LEN_UUID_STR + 1];
+};
+
static GSList *csc_adapters = NULL;

static gint cmp_adapter(gconstpointer a, gconstpointer b)
@@ -112,6 +118,76 @@ static void destroy_csc(gpointer user_data)
g_free(csc);
}

+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 = NULL;
+ uint8_t format;
+ int i;
+
+ if (status != 0) {
+ error("Discover %s descriptors failed: %s", ch->uuid,
+ att_ecode2str(status));
+ goto done;
+ }
+
+ list = dec_find_info_resp(pdu, len, &format);
+ if (list == NULL)
+ goto done;
+
+ 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)
+ continue;
+
+ if (g_strcmp0(ch->uuid, CSC_MEASUREMENT_UUID) == 0)
+ ch->csc->measurement_ccc_handle = handle;
+
+ /* We only want CCC, can break here */
+ break;
+ }
+
+done:
+ if (list)
+ att_data_list_free(list);
+ g_free(ch);
+}
+
+static void discover_desc(struct csc *csc, struct gatt_char *c,
+ struct gatt_char *c_next)
+{
+ struct characteristic *ch;
+ 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 != csc->svc_range->end) {
+ end = csc->svc_range->end;
+ } else {
+ return;
+ }
+
+ ch = g_new0(struct characteristic, 1);
+ ch->csc = csc;
+ memcpy(ch->uuid, c->uuid, sizeof(c->uuid));
+
+ gatt_find_info(csc->attrib, start, end, discover_desc_cb, ch);
+}
+
static void discover_char_cb(GSList *chars, guint8 status, gpointer user_data)
{
struct csc *csc = user_data;
@@ -124,9 +200,11 @@ static void discover_char_cb(GSList *chars, guint8 status, gpointer user_data)

for (; chars; chars = chars->next) {
struct gatt_char *c = chars->data;
+ struct gatt_char *c_next =
+ (chars->next ? chars->next->data : NULL);

if (g_strcmp0(c->uuid, CSC_MEASUREMENT_UUID) == 0) {
- /* TODO: discover CCC handle */
+ discover_desc(csc, c, c_next);
} else if (g_strcmp0(c->uuid, CSC_FEATURE_UUID) == 0) {
/* TODO: read characterictic value */
} else if (g_strcmp0(c->uuid, SENSOR_LOCATION_UUID) == 0) {
@@ -135,7 +213,7 @@ static void discover_char_cb(GSList *chars, guint8 status, gpointer user_data)
} else if (g_strcmp0(c->uuid, SC_CONTROL_POINT_UUID) == 0) {
DBG("SC Control Point supported");
csc->controlpoint_val_handle = c->value_handle;
- /* TODO: discover CCC handle */
+ discover_desc(csc, c, c_next);
}
}
}
--
1.8.0


2012-12-04 12:43:27

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [PATCH v3 01/20] cyclingspeed: Add CSC profile plugin skeleton

This patch adds stub profile driver plugin for CSC profile.
---
Makefile.am | 5 +-
lib/uuid.h | 2 +
profiles/cyclingspeed/cyclingspeed.c | 192 +++++++++++++++++++++++++++++++++++
3 files changed, 197 insertions(+), 2 deletions(-)
create mode 100644 profiles/cyclingspeed/cyclingspeed.c

diff --git a/Makefile.am b/Makefile.am
index e95ba1a..d009828 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -178,7 +178,7 @@ builtin_sources += profiles/health/hdp_main.c profiles/health/hdp_types.h \
endif

builtin_modules += thermometer alert time gatt_example proximity deviceinfo \
- gatt scanparam heartrate
+ gatt scanparam heartrate cyclingspeed
builtin_sources += profiles/thermometer/main.c \
profiles/thermometer/manager.h \
profiles/thermometer/manager.c \
@@ -217,7 +217,8 @@ builtin_sources += profiles/thermometer/main.c \
profiles/heartrate/manager.c \
profiles/heartrate/manager.h \
profiles/heartrate/heartrate.c \
- profiles/heartrate/heartrate.h
+ profiles/heartrate/heartrate.h \
+ profiles/cyclingspeed/cyclingspeed.c


builtin_modules += formfactor
diff --git a/lib/uuid.h b/lib/uuid.h
index f85abbe..a9a3b13 100644
--- a/lib/uuid.h
+++ b/lib/uuid.h
@@ -74,6 +74,8 @@ extern "C" {
#define INTERMEDIATE_TEMPERATURE_UUID "00002a1e-0000-1000-8000-00805f9b34fb"
#define MEASUREMENT_INTERVAL_UUID "00002a21-0000-1000-8000-00805f9b34fb"

+#define CYCLING_SC_UUID "00001816-0000-1000-8000-00805f9b34fb"
+
#define RFCOMM_UUID_STR "00000003-0000-1000-8000-00805f9b34fb"

#define HDP_UUID "00001400-0000-1000-8000-00805f9b34fb"
diff --git a/profiles/cyclingspeed/cyclingspeed.c b/profiles/cyclingspeed/cyclingspeed.c
new file mode 100644
index 0000000..025b09a
--- /dev/null
+++ b/profiles/cyclingspeed/cyclingspeed.c
@@ -0,0 +1,192 @@
+/*
+ *
+ * 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 "plugin.h"
+#include "adapter.h"
+#include "device.h"
+#include "profile.h"
+
+struct csc_adapter {
+ struct btd_adapter *adapter;
+ GSList *devices; /* list of registered devices */
+};
+
+struct csc {
+ struct btd_device *dev;
+ struct csc_adapter *cadapter;
+};
+
+static GSList *csc_adapters = NULL;
+
+static gint cmp_adapter(gconstpointer a, gconstpointer b)
+{
+ const struct csc_adapter *cadapter = a;
+ const struct btd_adapter *adapter = b;
+
+ if (adapter == cadapter->adapter)
+ return 0;
+
+ return -1;
+}
+
+static gint cmp_device(gconstpointer a, gconstpointer b)
+{
+ const struct csc *csc = a;
+ const struct btd_device *dev = b;
+
+ if (dev == csc->dev)
+ return 0;
+
+ return -1;
+}
+
+static struct csc_adapter *find_csc_adapter(struct btd_adapter *adapter)
+{
+ GSList *l = g_slist_find_custom(csc_adapters, adapter, cmp_adapter);
+
+ if (!l)
+ return NULL;
+
+ return l->data;
+}
+
+static void destroy_csc_adapter(gpointer user_data)
+{
+ struct csc_adapter *cadapter = user_data;
+
+ g_free(cadapter);
+}
+
+static void destroy_csc(gpointer user_data)
+{
+ struct csc *csc = user_data;
+
+ btd_device_unref(csc->dev);
+ g_free(csc);
+}
+
+static int csc_adapter_probe(struct btd_profile *p, struct btd_adapter *adapter)
+{
+ struct csc_adapter *cadapter;
+
+ cadapter = g_new0(struct csc_adapter, 1);
+ cadapter->adapter = adapter;
+
+ csc_adapters = g_slist_prepend(csc_adapters, cadapter);
+
+ return 0;
+}
+
+static void csc_adapter_remove(struct btd_profile *p,
+ struct btd_adapter *adapter)
+{
+ struct csc_adapter *cadapter;
+
+ cadapter = find_csc_adapter(adapter);
+ if (cadapter == NULL)
+ return;
+
+ csc_adapters = g_slist_remove(csc_adapters, cadapter);
+
+ destroy_csc_adapter(cadapter);
+}
+
+static int csc_device_probe(struct btd_profile *p,
+ struct btd_device *device, GSList *uuids)
+{
+ struct btd_adapter *adapter;
+ struct csc_adapter *cadapter;
+ struct csc *csc;
+
+ adapter = device_get_adapter(device);
+
+ cadapter = find_csc_adapter(adapter);
+ if (cadapter == NULL)
+ return -1;
+
+ csc = g_new0(struct csc, 1);
+ csc->dev = btd_device_ref(device);
+ csc->cadapter = cadapter;
+
+ cadapter->devices = g_slist_prepend(cadapter->devices, csc);
+
+ return 0;
+}
+
+static void csc_device_remove(struct btd_profile *p,
+ struct btd_device *device)
+{
+ struct btd_adapter *adapter;
+ struct csc_adapter *cadapter;
+ struct csc *csc;
+ GSList *l;
+
+ adapter = device_get_adapter(device);
+
+ cadapter = find_csc_adapter(adapter);
+ if (cadapter == NULL)
+ return;
+
+ l = g_slist_find_custom(cadapter->devices, device, cmp_device);
+ if (l == NULL)
+ return;
+
+ csc = l->data;
+
+ cadapter->devices = g_slist_remove(cadapter->devices, csc);
+
+ destroy_csc(csc);
+}
+
+static struct btd_profile cscp_profile = {
+ .name = "Cycling Speed and Cadence GATT Driver",
+ .remote_uuids = BTD_UUIDS(CYCLING_SC_UUID),
+
+ .adapter_probe = csc_adapter_probe,
+ .adapter_remove = csc_adapter_remove,
+
+ .device_probe = csc_device_probe,
+ .device_remove = csc_device_remove,
+};
+
+static int cyclingspeed_init(void)
+{
+ return btd_profile_register(&cscp_profile);
+}
+
+static void cyclingspeed_exit(void)
+{
+ btd_profile_unregister(&cscp_profile);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(cyclingspeed, VERSION,
+ BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
+ cyclingspeed_init, cyclingspeed_exit)
--
1.8.0


2012-12-04 12:43:28

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [PATCH v3 02/20] cyclingspeed: Add attio callbacks

---
profiles/cyclingspeed/cyclingspeed.c | 36 ++++++++++++++++++++++++++++++++++++
1 file changed, 36 insertions(+)

diff --git a/profiles/cyclingspeed/cyclingspeed.c b/profiles/cyclingspeed/cyclingspeed.c
index 025b09a..e0f458d 100644
--- a/profiles/cyclingspeed/cyclingspeed.c
+++ b/profiles/cyclingspeed/cyclingspeed.c
@@ -33,6 +33,11 @@
#include "adapter.h"
#include "device.h"
#include "profile.h"
+#include "attrib/gattrib.h"
+#include "attrib/att.h"
+#include "attrib/gatt.h"
+#include "attio.h"
+#include "log.h"

struct csc_adapter {
struct btd_adapter *adapter;
@@ -42,6 +47,9 @@ struct csc_adapter {
struct csc {
struct btd_device *dev;
struct csc_adapter *cadapter;
+
+ GAttrib *attrib;
+ guint attioid;
};

static GSList *csc_adapters = NULL;
@@ -89,10 +97,35 @@ static void destroy_csc(gpointer user_data)
{
struct csc *csc = user_data;

+ if (csc->attioid > 0)
+ btd_device_remove_attio_callback(csc->dev, csc->attioid);
+
+ if (csc->attrib != NULL)
+ g_attrib_unref(csc->attrib);
+
btd_device_unref(csc->dev);
g_free(csc);
}

+static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
+{
+ struct csc *csc = user_data;
+
+ DBG("");
+
+ csc->attrib = g_attrib_ref(attrib);
+}
+
+static void attio_disconnected_cb(gpointer user_data)
+{
+ struct csc *csc = user_data;
+
+ DBG("");
+
+ g_attrib_unref(csc->attrib);
+ csc->attrib = NULL;
+}
+
static int csc_adapter_probe(struct btd_profile *p, struct btd_adapter *adapter)
{
struct csc_adapter *cadapter;
@@ -138,6 +171,9 @@ static int csc_device_probe(struct btd_profile *p,

cadapter->devices = g_slist_prepend(cadapter->devices, csc);

+ csc->attioid = btd_device_add_attio_callback(device, attio_connected_cb,
+ attio_disconnected_cb, csc);
+
return 0;
}

--
1.8.0