2015-07-02 14:33:41

by Vinicius Costa Gomes

[permalink] [raw]
Subject: [RFC BlueZ 00/13] Introducing Soletta peripheral node-type

Hi,

We have been working on Soletta[1] for a few months and now that it has
been open sourced, it makes sense to show what it can do using
BlueZ. This implements a very simple node type that behaves as a Heart
Rate Sensor in the peripheral mode.

Just a overview about Soletta, it provides a way to write IoT
applications targeting a broad range of devices (from the smallest
edge to the smarttest hub).

You would express your application logic using a Flow Based language
to connect various nodes, write your custom node types using the
libsolleta C API. And the Soletta developers would worry about porting
the libsoletta library to the multitude of targets and providing a
useful collection of node types.

Using the support we already have in BlueZ for the Bluetooth LE
peripheral mode, writing a Soletta node type for the Heartrate Sensor
role seems a very good match. And it brings some very interesting use
cases with the linux-micro target.

The way I thought best about integrating BlueZ with Soletta was to add
another mainloop backend using libsoletta so every event would be
handled consistently. See patch 02/13.

Most of the changes not related to either adding the mainloop support,
or adding the node type code are related to some lack of
configurability to the peripheral code, perhaps having a way to the
callback that will register the service to have a say in some
parameters?

The most important question is: is BlueZ the right place to have this code?

What do you say?

Any questions about the project, we are hanging out in #soletta @ freenode.net.

Cheers,

[1] https://github.com/solettaproject/soletta

Vinicius Costa Gomes (13):
build: Add configure-time checks for soletta
shared: Add a mainloop implementation using soletta
peripheral/gatt: Fix usage of mainloop_ functions
peripheral/gatt: Add a way to external services to register services
peripheral/gap: Fix missing includes
peripheral/gap: Init the gatt_server
peripheral: Disable support for static random addresses
peripheral/gap: Set the discoverable flag in the advertising
peripheral/gatt: Use LOW security level
soletta/heartrate: Add a node-type for the Heartrate profile
.gitignore: Ignore soletta generated files
build: Add heartrate soletta node type to the build system
soletta: Add a sample flow using the heartrate node

.gitignore | 4 +
Makefile.am | 7 +-
Makefile.tools | 20 +++
configure.ac | 38 ++++++
peripheral/gap.c | 12 +-
peripheral/gatt.c | 124 ++++++++++++-----
peripheral/gatt.h | 6 +
soletta/heartrate-src.json | 30 +++++
soletta/heartrate.c | 298 ++++++++++++++++++++++++++++++++++++++++
soletta/heartrate.fbp | 1 +
soletta/sol-flow.conf | 2 +
src/shared/io-soletta.c | 315 +++++++++++++++++++++++++++++++++++++++++++
src/shared/timeout-soletta.c | 112 +++++++++++++++
13 files changed, 934 insertions(+), 35 deletions(-)
create mode 100644 soletta/heartrate-src.json
create mode 100644 soletta/heartrate.c
create mode 100644 soletta/heartrate.fbp
create mode 100644 soletta/sol-flow.conf
create mode 100644 src/shared/io-soletta.c
create mode 100644 src/shared/timeout-soletta.c

--
2.4.5


2015-07-03 17:14:57

by Vinicius Costa Gomes

[permalink] [raw]
Subject: Re: [RFC BlueZ 00/13] Introducing Soletta peripheral node-type

Hi Luiz,

Luiz Augusto von Dentz <[email protected]> writes:

> Hi Vinicius,
>
> On Thu, Jul 2, 2015 at 5:33 PM, Vinicius Costa Gomes <[email protected]> wrote:
>> Hi,
>>
>> We have been working on Soletta[1] for a few months and now that it has
>> been open sourced, it makes sense to show what it can do using
>> BlueZ. This implements a very simple node type that behaves as a Heart
>> Rate Sensor in the peripheral mode.
>>
>> Just a overview about Soletta, it provides a way to write IoT
>> applications targeting a broad range of devices (from the smallest
>> edge to the smarttest hub).
>>
>> You would express your application logic using a Flow Based language
>> to connect various nodes, write your custom node types using the
>> libsolleta C API. And the Soletta developers would worry about porting
>> the libsoletta library to the multitude of targets and providing a
>> useful collection of node types.
>>
>> Using the support we already have in BlueZ for the Bluetooth LE
>> peripheral mode, writing a Soletta node type for the Heartrate Sensor
>> role seems a very good match. And it brings some very interesting use
>> cases with the linux-micro target.
>
> I guess it would be a good idea to have a README in peripheral
> directory explaining the setup/architecture, etc, much like we did for
> Android.

Of course. I absolutely agree.

>
>> The way I thought best about integrating BlueZ with Soletta was to add
>> another mainloop backend using libsoletta so every event would be
>> handled consistently. See patch 02/13.
>>
>> Most of the changes not related to either adding the mainloop support,
>> or adding the node type code are related to some lack of
>> configurability to the peripheral code, perhaps having a way to the
>> callback that will register the service to have a say in some
>> parameters?
>>
>> The most important question is: is BlueZ the right place to have this code?
>
> It seems like it as long as we don't make our shared code public, as a
> shared library, I believe BlueZ will be the right place.
>

Cool.

>> What do you say?
>
> I suppose we need a bit more information regarding what kind of setups
> this is targeting, I suppose this is to be run on systems without
> D-Bus, something like all-in-one design? I don't mind having another
> mainloop but the unit tests are currently limited to glib only so it
> won't catch bugs/regressions with other mainloops perhaps we need to
> address this.

The setup that this series is targeting is the all-in-one one. And as it
was the most intrusive (from the BlueZ perspective) it made sense to
start from it. And it aligns nicely with Soletta having a PID 1
implementation.

(I will take a look and see what I can do about the tests.)

>
> The IoT setups seems to be pretty open right now, from what I heard
> there are setups that would run systemd+bluetoothd(dual-mode/le-only),
> bluetoothd(dual-mode/le-only), all-in-one(le-only) setups, for le-only
> setups Im afraid we will need to make the code even more modular to
> reduce the memory footprint, so perhaps we should nail down what
> setups really make sense to be supported upstream

Just for reference, we tried to see how small the binary would be for
this, we got to an ~180KB static binary when linking the heartrate
module with the soletta PID 1 implementation, but did not measure memory
consumption. Will put together the steps to build this in the
README.

But I still can see interesting use cases for having some (mostly the
client side of GATT profiles) profiles implemented inside the Soletta
project (using the D-Bus APIs). This being the bluetoothd+systemd setup,
I presume.

The bluetoothd(dual-mode/le-only) case I wasn't thinking about it, and I
still can't think of use cases for it, I guess I didn't understand it.

>
> For example setups that require classic will very likely require audio
> or/and obexd which probably pulls everything we have, so I guess it
> makes little sense to put any effort making this part of the code more
> memory efficient.

I agree.

>
>> Any questions about the project, we are hanging out in #soletta @ freenode.net.
>>
>> Cheers,
>>
>> [1] https://github.com/solettaproject/soletta
>>
>> Vinicius Costa Gomes (13):
>> build: Add configure-time checks for soletta
>> shared: Add a mainloop implementation using soletta
>> peripheral/gatt: Fix usage of mainloop_ functions
>> peripheral/gatt: Add a way to external services to register services
>> peripheral/gap: Fix missing includes
>> peripheral/gap: Init the gatt_server
>> peripheral: Disable support for static random addresses
>> peripheral/gap: Set the discoverable flag in the advertising
>> peripheral/gatt: Use LOW security level
>> soletta/heartrate: Add a node-type for the Heartrate profile
>> .gitignore: Ignore soletta generated files
>> build: Add heartrate soletta node type to the build system
>> soletta: Add a sample flow using the heartrate node
>>
>> .gitignore | 4 +
>> Makefile.am | 7 +-
>> Makefile.tools | 20 +++
>> configure.ac | 38 ++++++
>> peripheral/gap.c | 12 +-
>> peripheral/gatt.c | 124 ++++++++++++-----
>> peripheral/gatt.h | 6 +
>> soletta/heartrate-src.json | 30 +++++
>> soletta/heartrate.c | 298 ++++++++++++++++++++++++++++++++++++++++
>> soletta/heartrate.fbp | 1 +
>> soletta/sol-flow.conf | 2 +
>
> Id put soletta code under peripheral/soletta as it appears to depend
> on that anyway.

Yes, it depends on it. Sure.


Cheers,
--
Vinicius

2015-07-03 08:09:21

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: Re: [RFC BlueZ 00/13] Introducing Soletta peripheral node-type

Hi Vinicius,

On Thu, Jul 2, 2015 at 5:33 PM, Vinicius Costa Gomes <[email protected]> wrote:
> Hi,
>
> We have been working on Soletta[1] for a few months and now that it has
> been open sourced, it makes sense to show what it can do using
> BlueZ. This implements a very simple node type that behaves as a Heart
> Rate Sensor in the peripheral mode.
>
> Just a overview about Soletta, it provides a way to write IoT
> applications targeting a broad range of devices (from the smallest
> edge to the smarttest hub).
>
> You would express your application logic using a Flow Based language
> to connect various nodes, write your custom node types using the
> libsolleta C API. And the Soletta developers would worry about porting
> the libsoletta library to the multitude of targets and providing a
> useful collection of node types.
>
> Using the support we already have in BlueZ for the Bluetooth LE
> peripheral mode, writing a Soletta node type for the Heartrate Sensor
> role seems a very good match. And it brings some very interesting use
> cases with the linux-micro target.

I guess it would be a good idea to have a README in peripheral
directory explaining the setup/architecture, etc, much like we did for
Android.

> The way I thought best about integrating BlueZ with Soletta was to add
> another mainloop backend using libsoletta so every event would be
> handled consistently. See patch 02/13.
>
> Most of the changes not related to either adding the mainloop support,
> or adding the node type code are related to some lack of
> configurability to the peripheral code, perhaps having a way to the
> callback that will register the service to have a say in some
> parameters?
>
> The most important question is: is BlueZ the right place to have this code?

It seems like it as long as we don't make our shared code public, as a
shared library, I believe BlueZ will be the right place.

> What do you say?

I suppose we need a bit more information regarding what kind of setups
this is targeting, I suppose this is to be run on systems without
D-Bus, something like all-in-one design? I don't mind having another
mainloop but the unit tests are currently limited to glib only so it
won't catch bugs/regressions with other mainloops perhaps we need to
address this.

The IoT setups seems to be pretty open right now, from what I heard
there are setups that would run systemd+bluetoothd(dual-mode/le-only),
bluetoothd(dual-mode/le-only), all-in-one(le-only) setups, for le-only
setups Im afraid we will need to make the code even more modular to
reduce the memory footprint, so perhaps we should nail down what
setups really make sense to be supported upstream

For example setups that require classic will very likely require audio
or/and obexd which probably pulls everything we have, so I guess it
makes little sense to put any effort making this part of the code more
memory efficient.

> Any questions about the project, we are hanging out in #soletta @ freenode.net.
>
> Cheers,
>
> [1] https://github.com/solettaproject/soletta
>
> Vinicius Costa Gomes (13):
> build: Add configure-time checks for soletta
> shared: Add a mainloop implementation using soletta
> peripheral/gatt: Fix usage of mainloop_ functions
> peripheral/gatt: Add a way to external services to register services
> peripheral/gap: Fix missing includes
> peripheral/gap: Init the gatt_server
> peripheral: Disable support for static random addresses
> peripheral/gap: Set the discoverable flag in the advertising
> peripheral/gatt: Use LOW security level
> soletta/heartrate: Add a node-type for the Heartrate profile
> .gitignore: Ignore soletta generated files
> build: Add heartrate soletta node type to the build system
> soletta: Add a sample flow using the heartrate node
>
> .gitignore | 4 +
> Makefile.am | 7 +-
> Makefile.tools | 20 +++
> configure.ac | 38 ++++++
> peripheral/gap.c | 12 +-
> peripheral/gatt.c | 124 ++++++++++++-----
> peripheral/gatt.h | 6 +
> soletta/heartrate-src.json | 30 +++++
> soletta/heartrate.c | 298 ++++++++++++++++++++++++++++++++++++++++
> soletta/heartrate.fbp | 1 +
> soletta/sol-flow.conf | 2 +

Id put soletta code under peripheral/soletta as it appears to depend
on that anyway.

> src/shared/io-soletta.c | 315 +++++++++++++++++++++++++++++++++++++++++++
> src/shared/timeout-soletta.c | 112 +++++++++++++++
> 13 files changed, 934 insertions(+), 35 deletions(-)
> create mode 100644 soletta/heartrate-src.json
> create mode 100644 soletta/heartrate.c
> create mode 100644 soletta/heartrate.fbp
> create mode 100644 soletta/sol-flow.conf
> create mode 100644 src/shared/io-soletta.c
> create mode 100644 src/shared/timeout-soletta.c
>
> --
> 2.4.5
> --
> To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html



--
Luiz Augusto von Dentz

2015-07-02 14:33:54

by Vinicius Costa Gomes

[permalink] [raw]
Subject: [RFC BlueZ 13/13] soletta: Add a sample flow using the heartrate node

The flow and associated conffile, defines a very simple flow using the
heartrate node.

With soletta already installed you just run it, something like this:

$ sol-fbp-runner heartrate.fbp

It will look for the sol-flow.conf (which would define options for the
nodes) in the current directory.
---
soletta/heartrate.fbp | 1 +
soletta/sol-flow.conf | 2 ++
2 files changed, 3 insertions(+)
create mode 100644 soletta/heartrate.fbp
create mode 100644 soletta/sol-flow.conf

diff --git a/soletta/heartrate.fbp b/soletta/heartrate.fbp
new file mode 100644
index 0000000..63185c4
--- /dev/null
+++ b/soletta/heartrate.fbp
@@ -0,0 +1 @@
+Timer(timer:interval=1000) OUT -> IN Heartrate(heartrate)
diff --git a/soletta/sol-flow.conf b/soletta/sol-flow.conf
new file mode 100644
index 0000000..08ea12e
--- /dev/null
+++ b/soletta/sol-flow.conf
@@ -0,0 +1,2 @@
+[SolettaNodeEntry Heartrate]
+Type=heartrate
--
2.4.5


2015-07-02 14:33:53

by Vinicius Costa Gomes

[permalink] [raw]
Subject: [RFC BlueZ 12/13] build: Add heartrate soletta node type to the build system

---
Makefile.tools | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)

diff --git a/Makefile.tools b/Makefile.tools
index 1899fcb..9b330cb 100644
--- a/Makefile.tools
+++ b/Makefile.tools
@@ -73,6 +73,26 @@ peripheral_btsensor_SOURCES = peripheral/main.c \
peripheral_btsensor_LDADD = src/libshared-mainloop.la \
lib/libbluetooth-internal.la

+if SOLETTA
+soletta_descdir = @SOLETTA_DESCDIR@/flow/descriptions/
+soletta_desc_DATA = soletta/heartrate.json
+solettamodulesdir = @SOLETTA_MODULESDIR@/flow
+solettamodules_LTLIBRARIES = soletta/heartrate.la
+soletta_heartrate_la_SOURCES = soletta/heartrate.c peripheral/gap.h peripheral/gap.c \
+ peripheral/gatt.h peripheral/gatt.c
+soletta_heartrate_la_LDFLAGS = $(AM_LDFLAGS) -module -avoid-version
+soletta_heartrate_la_LIBADD = src/libshared-soletta.la \
+ lib/libbluetooth-internal.la \
+ @SOLETTA_LIBS@
+soletta_heartrate_la_CFLAGS = $(AM_CFLAGS) @SOLETTA_CFLAGS@ -DSOL_FLOW_NODE_TYPE_MODULE_EXTERNAL
+
+soletta/%-gen.c soletta/%-gen.h: soletta/heartrate-src.json
+ $(AM_V_GEN)$(PYTHON) @SOLETTA_PREFIX@/bin/sol-flow-node-type-gen.py $< soletta/$*-gen.h soletta/$*-gen.c soletta/heartrate.json
+
+BUILT_SOURCES += soletta/heartrate-gen.c soletta/heartrate-gen.h
+
+endif
+
tools_3dsp_SOURCES = tools/3dsp.c monitor/bt.h
tools_3dsp_LDADD = src/libshared-mainloop.la

--
2.4.5


2015-07-02 14:33:52

by Vinicius Costa Gomes

[permalink] [raw]
Subject: [RFC BlueZ 11/13] .gitignore: Ignore soletta generated files

---
.gitignore | 4 ++++
1 file changed, 4 insertions(+)

diff --git a/.gitignore b/.gitignore
index 7c1b7e0..6958ce9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -154,6 +154,10 @@ android/test-ipc
android/test-*.log
android/test-*.trs

+soletta/heartrate-gen.c
+soletta/heartrate-gen.h
+soletta/heartrate.json
+
cscope.in.out
cscope.out
cscope.po.out
--
2.4.5


2015-07-02 14:33:51

by Vinicius Costa Gomes

[permalink] [raw]
Subject: [RFC BlueZ 10/13] soletta/heartrate: Add a node-type for the Heartrate profile

This simple node type exports a node with only a input port that
receives pulses that will be exported via the Heartrate service as beats
per minute.
---
soletta/heartrate-src.json | 30 +++++
soletta/heartrate.c | 298 +++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 328 insertions(+)
create mode 100644 soletta/heartrate-src.json
create mode 100644 soletta/heartrate.c

diff --git a/soletta/heartrate-src.json b/soletta/heartrate-src.json
new file mode 100644
index 0000000..3db72ce
--- /dev/null
+++ b/soletta/heartrate-src.json
@@ -0,0 +1,30 @@
+{
+ "name": "Bluetooth",
+ "meta": {
+ "author": "Intel Corporation",
+ "version": "1"
+ },
+ "types": [
+ {
+ "category": "output/hw",
+ "description": "Bluetooth Smart Heart Rate Profile server",
+ "in_ports": [
+ {
+ "data_type": "any",
+ "description": "each packet is a beat",
+ "methods": {
+ "process": "heartrate_in_process"
+ },
+ "name": "IN"
+ }
+ ],
+ "methods": {
+ "close": "heartrate_close",
+ "open": "heartrate_open"
+ },
+ "name": "heartrate",
+ "private_data_type": "heartrate_data",
+ "url": "http://soletta.org/doc/latest/node_types/heartrate.html"
+ }
+ ]
+}
diff --git a/soletta/heartrate.c b/soletta/heartrate.c
new file mode 100644
index 0000000..4abcd95
--- /dev/null
+++ b/soletta/heartrate.c
@@ -0,0 +1,298 @@
+ #include "heartrate-gen.h"
+
+#include <errno.h>
+#include <stdio.h>
+#include <bluetooth.h>
+#include <l2cap.h>
+
+#include <uuid.h>
+
+#include "src/shared/queue.h"
+#include "src/shared/att.h"
+#include "src/shared/gatt-db.h"
+#include "src/shared/timeout.h"
+#include "src/shared/util.h"
+
+#include "peripheral/gap.h"
+#include "peripheral/gatt.h"
+
+#include <soletta/sol-flow.h>
+#include <soletta/sol-log.h>
+
+/* In seconds */
+#define WINDOW_SIZE 5
+
+struct heartrate_data {
+ struct gatt_db_attribute *service;
+ struct queue *to_notify;
+ int notifier;
+ int beats_per_window;
+ unsigned int disconnect_watch;
+ uint16_t handle;
+};
+
+static struct queue *sensors;
+
+static struct gatt_db *gatt_db;
+
+static int heartrate_in_process(struct sol_flow_node *node, void *data,
+ uint16_t port, uint16_t conn_id,
+ const struct sol_flow_packet *packet)
+{
+ struct heartrate_data *hr = data;
+
+ if (queue_isempty(hr->to_notify))
+ return 0;
+
+ hr->beats_per_window++;
+
+ return 0;
+}
+
+static bool bluetooth_init(void)
+{
+ gap_start();
+
+ sensors = queue_new();
+ if (!sensors)
+ return false;
+
+ return true;
+}
+
+static bool match_att(const void *data, const void *match_data) {
+ fprintf(stderr, "data %p == %p match_data\n", data, match_data);
+
+ return data == match_data;
+}
+
+static void ccc_read(struct gatt_db_attribute *attrib, unsigned int id,
+ uint16_t offset, uint8_t opcode, struct bt_att *att,
+ void *user_data)
+{
+ struct heartrate_data *hr = user_data;
+ uint8_t value[2] = { 0 };
+
+ if (queue_find(hr->to_notify, match_att, att))
+ value[0] = 0x01;
+
+ gatt_db_attribute_read_result(attrib, id, 0, value, sizeof(value));
+}
+
+static void remove_att(void *data, void *user_data)
+{
+ struct heartrate_data *hr = data;
+ struct bt_att *att = user_data;
+
+ fprintf(stderr, "[remove_att] hr %p notifier %d\n", hr, hr->notifier);
+
+ if (!queue_find(hr->to_notify, match_att, att))
+ return;
+
+ queue_remove(hr->to_notify, att);
+
+ bt_att_unref(att);
+
+ if (queue_isempty(hr->to_notify)) {
+ timeout_remove(hr->notifier);
+ hr->notifier = -1;
+ }
+}
+
+static void remove_notify_cb(int err, void *user_data)
+{
+ struct bt_att *att = user_data;
+
+ fprintf(stderr, "remove_notify_cb att %p\n", att);
+
+ queue_foreach(sensors, remove_att, att);
+}
+
+static bool send_notification(void *user_data)
+{
+ struct heartrate_data *hr = user_data;
+ const struct queue_entry *e;
+ uint8_t pdu[4];
+
+ fprintf(stderr, "[send_notification] hr %p notifier %d\n", hr, hr->notifier);
+
+ e = queue_get_entries(hr->to_notify);
+ if (!e)
+ return true;
+
+ put_le16(hr->handle, &pdu[0]);
+
+ pdu[2] = 0x06;
+ pdu[3] = hr->beats_per_window * (60 / WINDOW_SIZE);
+
+ hr->beats_per_window = 0;
+
+ while (e) {
+ struct bt_att *att = e->data;
+ bt_att_send(att, BT_ATT_OP_HANDLE_VAL_NOT, pdu, sizeof(pdu), NULL, NULL, NULL);
+ e = e->next;
+ }
+
+ return true;
+}
+
+static void enable_notification(struct heartrate_data *hr, struct bt_att *att)
+{
+ fprintf(stderr, "[enable_notification] hr %p notifier %d\n", hr, hr->notifier);
+
+ if (queue_find(hr->to_notify, match_att, att)) {
+ return;
+ }
+
+ if (!queue_push_tail(hr->to_notify, bt_att_ref(att))) {
+ bt_att_unref(att);
+ return;
+ }
+
+ fprintf(stderr, "add notify %p\n", att);
+
+ hr->disconnect_watch = bt_att_register_disconnect(att, remove_notify_cb, att, NULL);
+
+ fprintf(stderr, "notifier %d\n", hr->notifier);
+
+ if (hr->notifier != -1)
+ return;
+
+ hr->notifier = timeout_add(WINDOW_SIZE * 1000, send_notification, hr, NULL);
+}
+
+static void disable_notification(struct heartrate_data *hr, struct bt_att *att)
+{
+ if (!queue_remove(hr->to_notify, att))
+ return;
+
+ bt_att_unregister_disconnect(att, hr->disconnect_watch);
+
+ bt_att_unref(att);
+
+ if (queue_isempty(hr->to_notify)) {
+ timeout_remove(hr->notifier);
+ hr->notifier = -1;
+ }
+}
+
+static void ccc_write(struct gatt_db_attribute *attrib, unsigned int id,
+ uint16_t offset, const uint8_t *value, size_t len,
+ uint8_t opcode, struct bt_att *att, void *user_data)
+{
+ struct heartrate_data *hr = user_data;
+ uint8_t ecode = 0;
+
+ fprintf(stderr, "[cc_write] hr %p notifier %d\n", hr, hr->notifier);
+
+ if (!value || len != 2) {
+ ecode = BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN;
+ goto done;
+ }
+
+ if (offset) {
+ ecode = BT_ATT_ERROR_INVALID_OFFSET;
+ goto done;
+ }
+
+ if (value[0] == 0x01)
+ enable_notification(hr, att);
+ else if (value[0] == 0x00)
+ disable_notification(hr, att);
+
+done:
+ gatt_db_attribute_write_result(attrib, id, ecode);
+}
+
+static void heartrate_register(struct gatt_db *db, void *user_data)
+{
+ struct heartrate_data *hr = user_data;
+ struct gatt_db_attribute *service, *chr;
+ bt_uuid_t uuid;
+
+ fprintf(stderr, "registering hr service\n");
+
+ gatt_db = gatt_db_ref(db);
+
+ bt_uuid16_create(&uuid, 0x180d);
+
+ service = gatt_db_add_service(gatt_db, &uuid, true, 6);
+ fprintf(stderr, "service %p\n", service);
+
+ /* Heart Rate Measurement characteristic */
+ bt_uuid16_create(&uuid, 0x2a37);
+ chr = gatt_db_service_add_characteristic(service, &uuid,
+ BT_ATT_PERM_NONE,
+ BT_GATT_CHRC_PROP_NOTIFY,
+ NULL, NULL, NULL);
+ fprintf(stderr, "chr %p\n", chr);
+
+ hr->handle = gatt_db_attribute_get_handle(chr);
+ fprintf(stderr, "handle %d\n", hr->handle);
+
+ /* Heart Rate CCC descriptor */
+ bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID);
+ gatt_db_service_add_descriptor(chr, &uuid,
+ BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+ ccc_read, ccc_write, hr);
+
+ hr->service = service;
+
+ gatt_db_service_set_active(service, true);
+
+ fprintf(stderr, "[register] hr %p notifier %d\n", hr, hr->notifier);
+}
+
+static int heartrate_open(struct sol_flow_node *node, void *data,
+ const struct sol_flow_node_options *options)
+{
+ static bool initialized = false;
+ struct heartrate_data *hr = data;
+
+ if (!initialized) {
+ initialized = bluetooth_init();
+
+ if (!initialized) {
+ SOL_WRN("Could not initialize Bluetooth module");
+ return -EINVAL;
+ }
+ }
+
+ /* FIXME: remove when supporting multiple locations */
+ if (!queue_isempty(sensors))
+ return 0;
+
+ hr->to_notify = queue_new();
+ hr->notifier = -1;
+
+ fprintf(stderr, "[open] hr %p notifier %d\n", hr, hr->notifier);
+
+ gatt_server_add_service(heartrate_register, hr);
+
+ queue_push_tail(sensors, hr);
+
+ return 0;
+}
+
+static void heartrate_close(struct sol_flow_node *node, void *data)
+{
+ struct heartrate_data *hr = data;
+
+ if (hr->service)
+ gatt_db_remove_service(gatt_db, hr->service);
+
+ hr->service = NULL;
+
+ queue_remove_all(hr->to_notify, NULL, bt_att_unref, NULL);
+
+ if (hr->notifier >= 0)
+ timeout_remove(hr->notifier);
+
+ hr->notifier = -1;
+
+ queue_remove(sensors, hr);
+
+ gatt_db_unref(gatt_db);
+}
+
+#include "heartrate-gen.c"
--
2.4.5


2015-07-02 14:33:50

by Vinicius Costa Gomes

[permalink] [raw]
Subject: [RFC BlueZ 09/13] peripheral/gatt: Use LOW security level

Mostly to help testing. And for heartrate, the profile being implemented
in later patches, this is a valid security level for useful use cases.
---
peripheral/gatt.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/peripheral/gatt.c b/peripheral/gatt.c
index 0cb4f6d..0b5d5fe 100644
--- a/peripheral/gatt.c
+++ b/peripheral/gatt.c
@@ -134,7 +134,7 @@ static struct gatt_conn *gatt_conn_new(int fd)
bt_att_set_close_on_unref(conn->att, true);
bt_att_register_disconnect(conn->att, gatt_conn_disconnect, conn, NULL);

- bt_att_set_security(conn->att, BT_SECURITY_MEDIUM);
+ bt_att_set_security(conn->att, BT_SECURITY_LOW);

conn->gatt = bt_gatt_server_new(gatt_db, conn->att, mtu);
if (!conn->gatt) {
--
2.4.5


2015-07-02 14:33:49

by Vinicius Costa Gomes

[permalink] [raw]
Subject: [RFC BlueZ 08/13] peripheral/gap: Set the discoverable flag in the advertising

---
peripheral/gap.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/peripheral/gap.c b/peripheral/gap.c
index 9c45057..e95394c 100644
--- a/peripheral/gap.c
+++ b/peripheral/gap.c
@@ -98,7 +98,7 @@ static void add_advertising(uint16_t index)
memset(buf, 0, sizeof(*cp) + sizeof(ad));
cp = buf;
cp->instance = 0x01;
- cp->flags = cpu_to_le32((1 << 0) | (1 << 1) | (1 << 4));
+ cp->flags = cpu_to_le32((1 << 0) | (1 << 1) | (1 << 3) | (1 << 4));
cp->duration = cpu_to_le16(0);
cp->timeout = cpu_to_le16(0);
cp->adv_data_len = sizeof(ad);
--
2.4.5


2015-07-02 14:33:47

by Vinicius Costa Gomes

[permalink] [raw]
Subject: [RFC BlueZ 06/13] peripheral/gap: Init the gatt_server

This will init the list that will have all the callbacks that will
register the services not part of the core.
---
peripheral/gap.c | 2 ++
1 file changed, 2 insertions(+)

diff --git a/peripheral/gap.c b/peripheral/gap.c
index a78dc45..fe65700 100644
--- a/peripheral/gap.c
+++ b/peripheral/gap.c
@@ -535,6 +535,8 @@ void gap_start(void)
return;
}

+ gatt_server_init();
+
if (!mgmt_send(mgmt, MGMT_OP_READ_COMMANDS,
MGMT_INDEX_NONE, 0, NULL,
read_commands_complete, NULL, NULL)) {
--
2.4.5


2015-07-02 14:33:48

by Vinicius Costa Gomes

[permalink] [raw]
Subject: [RFC BlueZ 07/13] peripheral: Disable support for static random addresses

This is mostly to make testing easier. I don't have any device which
support this properly.
---
peripheral/gap.c | 2 +-
peripheral/gatt.c | 3 +--
2 files changed, 2 insertions(+), 3 deletions(-)

diff --git a/peripheral/gap.c b/peripheral/gap.c
index fe65700..9c45057 100644
--- a/peripheral/gap.c
+++ b/peripheral/gap.c
@@ -48,7 +48,7 @@ static bool adv_features = false;
static bool adv_instances = false;
static bool require_connectable = true;

-static uint8_t static_addr[6] = { 0x90, 0x78, 0x56, 0x34, 0x12, 0xc0 };
+static uint8_t static_addr[6] = { 0 };
static uint8_t dev_name[260] = { 0x00, };
static uint8_t dev_name_len = 0;

diff --git a/peripheral/gatt.c b/peripheral/gatt.c
index a859adc..0cb4f6d 100644
--- a/peripheral/gatt.c
+++ b/peripheral/gatt.c
@@ -285,8 +285,7 @@ void gatt_server_start(void)
memset(&addr, 0, sizeof(addr));
addr.l2_family = AF_BLUETOOTH;
addr.l2_cid = htobs(ATT_CID);
- memcpy(&addr.l2_bdaddr, static_addr, 6);
- addr.l2_bdaddr_type = BDADDR_LE_RANDOM;
+ addr.l2_bdaddr_type = BDADDR_LE_PUBLIC;

if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
fprintf(stderr, "Failed to bind ATT server socket: %m\n");
--
2.4.5


2015-07-02 14:33:46

by Vinicius Costa Gomes

[permalink] [raw]
Subject: [RFC BlueZ 05/13] peripheral/gap: Fix missing includes

---
peripheral/gap.c | 6 ++++++
1 file changed, 6 insertions(+)

diff --git a/peripheral/gap.c b/peripheral/gap.c
index f659f7f..a78dc45 100644
--- a/peripheral/gap.c
+++ b/peripheral/gap.c
@@ -27,9 +27,15 @@

#include <stdio.h>
#include <string.h>
+#include <stdbool.h>

#include "lib/bluetooth.h"
#include "lib/mgmt.h"
+#include "lib/uuid.h"
+#include "src/shared/att.h"
+#include "src/shared/queue.h"
+#include "src/shared/gatt-db.h"
+
#include "src/shared/util.h"
#include "src/shared/mgmt.h"
#include "peripheral/gatt.h"
--
2.4.5


2015-07-02 14:33:45

by Vinicius Costa Gomes

[permalink] [raw]
Subject: [RFC BlueZ 04/13] peripheral/gatt: Add a way to external services to register services

This will allow profiles implemented using the peripheral/ helpers to
register asynchronously their services. This cannot be done directly
because when gatt_service_start() the GATT database doesn't exist yet.
---
peripheral/gatt.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++
peripheral/gatt.h | 6 ++++++
2 files changed, 53 insertions(+)

diff --git a/peripheral/gatt.c b/peripheral/gatt.c
index a66292f..a859adc 100644
--- a/peripheral/gatt.c
+++ b/peripheral/gatt.c
@@ -54,6 +54,11 @@ struct gatt_conn {
struct bt_gatt_client *client;
};

+struct service_registerer_data {
+ gatt_service_register_t callback;
+ void *user_data;
+};
+
static struct io *att_io = NULL;
static struct queue *conn_list = NULL;
static struct gatt_db *gatt_db = NULL;
@@ -63,6 +68,8 @@ static uint8_t static_addr[6] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
static uint8_t dev_name[20];
static uint8_t dev_name_len = 0;

+static struct queue *registered_services = NULL;
+
void gatt_set_static_address(uint8_t addr[6])
{
memcpy(static_addr, addr, sizeof(static_addr));
@@ -243,6 +250,21 @@ static void populate_devinfo_service(struct gatt_db *db)
gatt_db_service_set_active(service, true);
}

+void gatt_server_init(void)
+{
+ registered_services = queue_new();
+}
+
+static void register_each_service(void *data, void *user_data)
+{
+ struct gatt_db *db = user_data;
+ struct service_registerer_data *registerer = data;
+
+ fprintf(stderr, "registering service %p\n", registerer);
+
+ registerer->callback(db, registerer->user_data);
+}
+
void gatt_server_start(void)
{
struct sockaddr_l2 addr;
@@ -287,6 +309,8 @@ void gatt_server_start(void)
populate_gap_service(gatt_db);
populate_devinfo_service(gatt_db);

+ queue_foreach(registered_services, register_each_service, gatt_db);
+
gatt_cache = gatt_db_new();

conn_list = queue_new();
@@ -323,10 +347,33 @@ void gatt_server_stop(void)

queue_destroy(conn_list, gatt_conn_destroy);

+ queue_destroy(registered_services, free);
+ registered_services = NULL;
+
gatt_db_unref(gatt_cache);
gatt_cache = NULL;

gatt_db_unref(gatt_db);
gatt_db = NULL;
+}
+
+int gatt_server_add_service(gatt_service_register_t callback, void *user_data)
+{
+ struct service_registerer_data *registerer;
+
+ fprintf(stderr, "gatt_server_add_service %p data %p\n", callback, user_data);
+
+ registerer = new0(struct service_registerer_data, 1);
+ if (!registerer)
+ return -ENOMEM;
+
+ registerer->callback = callback;
+ registerer->user_data = user_data;
+
+ if (!queue_push_tail(registered_services, registerer)) {
+ free(registerer);
+ return -ENOMEM;
+ }

+ return 0;
}
diff --git a/peripheral/gatt.h b/peripheral/gatt.h
index 5b68f35..5e16875 100644
--- a/peripheral/gatt.h
+++ b/peripheral/gatt.h
@@ -26,5 +26,11 @@
void gatt_set_static_address(uint8_t addr[6]);
void gatt_set_device_name(uint8_t name[20], uint8_t len);

+void gatt_server_init(void);
+
void gatt_server_start(void);
void gatt_server_stop(void);
+
+typedef void (*gatt_service_register_t)(struct gatt_db *db, void *user_data);
+
+int gatt_server_add_service(gatt_service_register_t callback, void *user_data);
--
2.4.5


2015-07-02 14:33:43

by Vinicius Costa Gomes

[permalink] [raw]
Subject: [RFC BlueZ 02/13] shared: Add a mainloop implementation using soletta

This will allow apps willing to integrate directly with the solleta
mainloop, with no need to implement custom ways (threads and pipes) to
communicate via Bluetooth and send/receive soletta flow packets.
---
Makefile.am | 7 +-
src/shared/io-soletta.c | 315 +++++++++++++++++++++++++++++++++++++++++++
src/shared/timeout-soletta.c | 112 +++++++++++++++
3 files changed, 433 insertions(+), 1 deletion(-)
create mode 100644 src/shared/io-soletta.c
create mode 100644 src/shared/timeout-soletta.c

diff --git a/Makefile.am b/Makefile.am
index 76d1723..f300d32 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -95,7 +95,7 @@ gdbus_libgdbus_internal_la_SOURCES = gdbus/gdbus.h \
gdbus/mainloop.c gdbus/watch.c \
gdbus/object.c gdbus/client.c gdbus/polkit.c

-noinst_LTLIBRARIES += src/libshared-glib.la src/libshared-mainloop.la
+noinst_LTLIBRARIES += src/libshared-glib.la src/libshared-mainloop.la src/libshared-soletta.la

shared_sources = src/shared/io.h src/shared/timeout.h \
src/shared/queue.h src/shared/queue.c \
@@ -128,6 +128,11 @@ src_libshared_mainloop_la_SOURCES = $(shared_sources) \
src/shared/io-mainloop.c \
src/shared/timeout-mainloop.c \
src/shared/mainloop.h src/shared/mainloop.c
+if SOLETTA
+src_libshared_soletta_la_SOURCES = $(shared_sources) \
+ src/shared/io-soletta.c \
+ src/shared/timeout-soletta.c
+endif

attrib_sources = attrib/att.h attrib/att-database.h attrib/att.c \
attrib/gatt.h attrib/gatt.c \
diff --git a/src/shared/io-soletta.c b/src/shared/io-soletta.c
new file mode 100644
index 0000000..1cc894a
--- /dev/null
+++ b/src/shared/io-soletta.c
@@ -0,0 +1,315 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2015 Intel Corporation. All rights reserved.
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 <stdio.h> /* FIXME: REMOVE ME */
+#include <unistd.h>
+#include <errno.h>
+#include <sys/socket.h>
+
+#include <soletta/sol-mainloop.h>
+
+#include "io.h"
+#include "mainloop.h"
+#include "util.h"
+
+struct io {
+ int fd;
+ int refcount;
+ unsigned int events;
+
+ bool close_on_destroy;
+
+ struct sol_fd *watch;
+
+ io_callback_func_t read_cb;
+ void *read_data;
+
+ io_callback_func_t write_cb;
+ void *write_data;
+
+ io_callback_func_t disconnect_cb;
+ void *disconnect_data;
+
+ io_destroy_func_t read_destroy;
+ io_destroy_func_t write_destroy;
+ io_destroy_func_t disconnect_destroy;
+};
+
+static bool io_event_cb(void *data, int fd, unsigned int events);
+
+static struct io *io_ref(struct io *io)
+{
+ if (!io)
+ return NULL;
+
+ __sync_fetch_and_add(&io->refcount, 1);
+
+ return io;
+}
+
+static void io_unref(struct io *io)
+{
+ if (!io)
+ return;
+
+ if (__sync_sub_and_fetch(&io->refcount, 1))
+ return;
+
+ free(io);
+}
+
+static void reset_io_watch(struct io *io, unsigned int events)
+{
+ if (!io)
+ return;
+
+ if (io->watch) {
+ sol_fd_del(io->watch);
+ io->watch = NULL;
+ }
+
+ if (events)
+ io->watch = sol_fd_add(io->fd, events, io_event_cb, io);
+
+ io->events = events;
+}
+
+static bool io_event_cb(void *data, int fd, unsigned int events)
+{
+ struct io *io = io_ref(data);
+ unsigned int old_events = io->events;
+ bool r = true;
+
+ if ((events & (SOL_FD_FLAGS_HUP | SOL_FD_FLAGS_ERR))) {
+ io->read_cb = NULL;
+ io->write_cb = NULL;
+
+ if (!io->disconnect_cb) {
+ sol_fd_del(io->watch);
+ io->watch = NULL;
+ return false;
+ }
+
+ if (!io->disconnect_cb(io, io->disconnect_data)) {
+ if (io->disconnect_destroy)
+ io->disconnect_destroy(io->disconnect_data);
+
+ io->disconnect_cb = NULL;
+ io->disconnect_destroy = NULL;
+ io->disconnect_data = NULL;
+
+ io->events &= ~SOL_FD_FLAGS_HUP;
+ }
+ }
+
+ if ((events & SOL_FD_FLAGS_IN) && io->read_cb) {
+ if (!io->read_cb(io, io->read_data)) {
+ if (io->read_destroy)
+ io->read_destroy(io->read_data);
+
+ io->read_cb = NULL;
+ io->read_destroy = NULL;
+ io->read_data = NULL;
+
+ io->events &= ~SOL_FD_FLAGS_IN;
+ }
+ }
+
+ if ((events & SOL_FD_FLAGS_OUT) && io->write_cb) {
+ if (!io->write_cb(io, io->write_data)) {
+ if (io->write_destroy)
+ io->write_destroy(io->write_data);
+
+ io->write_cb = NULL;
+ io->write_destroy = NULL;
+ io->write_data = NULL;
+
+ io->events &= ~SOL_FD_FLAGS_OUT;
+ }
+ }
+
+ if (old_events != io->events)
+ reset_io_watch(io, io->events);
+
+ if (!io->events) {
+ io->watch = NULL;
+ r = false;
+ }
+
+ io_unref(io);
+
+ return r;
+}
+
+struct io *io_new(int fd)
+{
+ struct io *io;
+
+ if (fd < 0)
+ return NULL;
+
+ io = new0(struct io, 1);
+ if (!io)
+ return NULL;
+
+ io->fd = fd;
+
+ return io_ref(io);
+}
+
+void io_destroy(struct io *io)
+{
+ if (!io)
+ return;
+
+ io->read_cb = NULL;
+ io->write_cb = NULL;
+ io->disconnect_cb = NULL;
+
+ sol_fd_del(io->watch);
+ io->watch = NULL;
+
+ io_unref(io);
+}
+
+int io_get_fd(struct io *io)
+{
+ if (!io)
+ return -ENOTCONN;
+
+ return io->fd;
+}
+
+bool io_set_close_on_destroy(struct io *io, bool do_close)
+{
+ if (!io)
+ return false;
+
+ io->close_on_destroy = do_close;
+
+ return true;
+}
+
+ssize_t io_send(struct io *io, const struct iovec *iov, int iovcnt)
+{
+ ssize_t ret;
+
+ if (!io || io->fd < 0)
+ return -ENOTCONN;
+
+ do {
+ ret = writev(io->fd, iov, iovcnt);
+ } while (ret < 0 && errno == EINTR);
+
+ if (ret < 0)
+ return -errno;
+
+ return ret;
+}
+
+bool io_shutdown(struct io *io)
+{
+ if (!io || io->fd < 0)
+ return false;
+
+ return shutdown(io->fd, SHUT_RDWR) == 0;
+}
+
+bool io_set_read_handler(struct io *io, io_callback_func_t callback,
+ void *user_data, io_destroy_func_t destroy)
+{
+ unsigned int events;
+
+ if (!io || io->fd < 0)
+ return false;
+
+ if (io->read_destroy)
+ io->read_destroy(io->read_data);
+
+ if (callback)
+ events = io->events | SOL_FD_FLAGS_IN;
+ else
+ events = io->events & ~SOL_FD_FLAGS_IN;
+
+ reset_io_watch(io, events);
+
+ io->read_cb = callback;
+ io->read_destroy = destroy;
+ io->read_data = user_data;
+
+ return true;
+}
+
+bool io_set_write_handler(struct io *io, io_callback_func_t callback,
+ void *user_data, io_destroy_func_t destroy)
+{
+ unsigned int events;
+
+ if (!io || io->fd < 0)
+ return false;
+
+ if (io->write_destroy)
+ io->write_destroy(io->write_data);
+
+ if (callback)
+ events = io->events | SOL_FD_FLAGS_OUT;
+ else
+ events = io->events & ~SOL_FD_FLAGS_OUT;
+
+ reset_io_watch(io, events);
+
+ io->write_cb = callback;
+ io->write_destroy = destroy;
+ io->write_data = user_data;
+
+ return true;
+}
+
+bool io_set_disconnect_handler(struct io *io, io_callback_func_t callback,
+ void *user_data, io_destroy_func_t destroy)
+{
+ unsigned int events;
+
+ if (!io || io->fd < 0)
+ return false;
+
+ if (io->disconnect_destroy)
+ io->disconnect_destroy(io->disconnect_data);
+
+ if (callback)
+ events = io->events | SOL_FD_FLAGS_HUP;
+ else
+ events = io->events & ~SOL_FD_FLAGS_HUP;
+
+ reset_io_watch(io, events);
+
+ io->disconnect_cb = callback;
+ io->disconnect_destroy = destroy;
+ io->disconnect_data = user_data;
+
+ return true;
+}
diff --git a/src/shared/timeout-soletta.c b/src/shared/timeout-soletta.c
new file mode 100644
index 0000000..2e72ac7
--- /dev/null
+++ b/src/shared/timeout-soletta.c
@@ -0,0 +1,112 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2015 Intel Corporation. All rights reserved.
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 <stdio.h> /* FIXME: remove me */
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/socket.h>
+
+#include <soletta/sol-mainloop.h>
+
+#include "timeout.h"
+#include "util.h"
+
+struct timeout_data {
+ struct sol_timeout *timeout;
+ timeout_func_t func;
+ timeout_destroy_func_t destroy;
+ void *user_data;
+};
+
+#define MAX_TIMEOUTS 32
+
+static struct timeout_data timeouts[MAX_TIMEOUTS];
+
+static bool timeout_callback(void *user_data)
+{
+ struct timeout_data *data = user_data;
+
+ if (data->func(data->user_data))
+ return true;
+
+ if (data->destroy)
+ data->destroy(data->user_data);
+
+ data->timeout = NULL;
+
+ return false;
+}
+
+unsigned int timeout_add(unsigned int timeout, timeout_func_t func,
+ void *user_data, timeout_destroy_func_t destroy)
+{
+ struct timeout_data *data;
+ int i;
+
+ if (!func)
+ return 0;
+
+ for (i = 0; i < MAX_TIMEOUTS; i++)
+ if (!timeouts[i].timeout)
+ break;
+
+ if (i == MAX_TIMEOUTS)
+ return 0;
+
+ data = &timeouts[i];
+
+ data->timeout = sol_timeout_add(timeout, timeout_callback, data);
+ if (!data->timeout)
+ return 0;
+
+ data->func = func;
+ data->destroy = destroy;
+ data->user_data = user_data;
+
+ fprintf(stderr, "timeout add %d\n", i);
+
+ return i + 1;
+}
+
+void timeout_remove(unsigned int id)
+{
+ struct timeout_data *data;
+
+ if (!id)
+ return;
+
+ if ((id - 1) >= MAX_TIMEOUTS)
+ return;
+
+ fprintf(stderr, "timeout remove %d\n", id - 1);
+
+ data = &timeouts[id - 1];
+
+ sol_timeout_del(data->timeout);
+ data->timeout = NULL;
+}
--
2.4.5


2015-07-02 14:33:44

by Vinicius Costa Gomes

[permalink] [raw]
Subject: [RFC BlueZ 03/13] peripheral/gatt: Fix usage of mainloop_ functions

If we want that this example is mainloop agnostic, it should use the
mainloop agnostic functions.
---
peripheral/gatt.c | 72 +++++++++++++++++++++++++++++++++----------------------
1 file changed, 43 insertions(+), 29 deletions(-)

diff --git a/peripheral/gatt.c b/peripheral/gatt.c
index 4c5531d..a66292f 100644
--- a/peripheral/gatt.c
+++ b/peripheral/gatt.c
@@ -25,6 +25,7 @@
#include <config.h>
#endif

+#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
@@ -34,7 +35,7 @@
#include "lib/bluetooth.h"
#include "lib/l2cap.h"
#include "lib/uuid.h"
-#include "src/shared/mainloop.h"
+#include "src/shared/io.h"
#include "src/shared/util.h"
#include "src/shared/queue.h"
#include "src/shared/att.h"
@@ -53,7 +54,7 @@ struct gatt_conn {
struct bt_gatt_client *client;
};

-static int att_fd = -1;
+static struct io *att_io = NULL;
static struct queue *conn_list = NULL;
static struct gatt_db *gatt_db = NULL;
static struct gatt_db *gatt_cache = NULL;
@@ -153,41 +154,43 @@ static struct gatt_conn *gatt_conn_new(int fd)
return conn;
}

-static void att_conn_callback(int fd, uint32_t events, void *user_data)
+static bool att_conn_callback(struct io *io, void *user_data)
{
struct gatt_conn *conn;
struct sockaddr_l2 addr;
socklen_t addrlen;
- int new_fd;
+ int fd, new_fd;

- if (events & (EPOLLERR | EPOLLHUP)) {
- mainloop_remove_fd(fd);
- return;
- }
+ fd = io_get_fd(io);
+
+ fprintf(stderr, "att_conn_callback io %p\n", io);

memset(&addr, 0, sizeof(addr));
addrlen = sizeof(addr);

- new_fd = accept(att_fd, (struct sockaddr *) &addr, &addrlen);
+ new_fd = accept(fd, (struct sockaddr *) &addr, &addrlen);
if (new_fd < 0) {
fprintf(stderr, "Failed to accept new ATT connection: %m\n");
- return;
+ return true;
}

conn = gatt_conn_new(new_fd);
if (!conn) {
fprintf(stderr, "Failed to create GATT connection\n");
close(new_fd);
- return;
+ return true;
}

if (!queue_push_tail(conn_list, conn)) {
fprintf(stderr, "Failed to add GATT connection\n");
gatt_conn_destroy(conn);
close(new_fd);
+ return true;
}

printf("New device connected\n");
+
+ return true;
}

static void gap_device_name_read(struct gatt_db_attribute *attrib,
@@ -243,13 +246,16 @@ static void populate_devinfo_service(struct gatt_db *db)
void gatt_server_start(void)
{
struct sockaddr_l2 addr;
+ int fd;
+
+ fprintf(stderr, "gatt_server_start\n");

- if (att_fd >= 0)
+ if (att_io)
return;

- att_fd = socket(PF_BLUETOOTH, SOCK_SEQPACKET | SOCK_CLOEXEC,
+ fd = socket(PF_BLUETOOTH, SOCK_SEQPACKET | SOCK_CLOEXEC | SOCK_NONBLOCK,
BTPROTO_L2CAP);
- if (att_fd < 0) {
+ if (fd < 0) {
fprintf(stderr, "Failed to create ATT server socket: %m\n");
return;
}
@@ -260,24 +266,21 @@ void gatt_server_start(void)
memcpy(&addr.l2_bdaddr, static_addr, 6);
addr.l2_bdaddr_type = BDADDR_LE_RANDOM;

- if (bind(att_fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
fprintf(stderr, "Failed to bind ATT server socket: %m\n");
- close(att_fd);
- att_fd = -1;
+ close(fd);
return;
}

- if (listen(att_fd, 1) < 0) {
+ if (listen(fd, 1) < 0) {
fprintf(stderr, "Failed to listen on ATT server socket: %m\n");
- close(att_fd);
- att_fd = -1;
+ close(fd);
return;
}

gatt_db = gatt_db_new();
if (!gatt_db) {
- close(att_fd);
- att_fd = -1;
+ close(fd);
return;
}

@@ -290,20 +293,33 @@ void gatt_server_start(void)
if (!conn_list) {
gatt_db_unref(gatt_db);
gatt_db = NULL;
- close(att_fd);
- att_fd = -1;
+ close(fd);
+ return;
+ }
+
+ att_io = io_new(fd);
+
+ fprintf(stderr, "att_io %p\n", att_io);
+
+ if (!att_io) {
+ gatt_db_unref(gatt_db);
+ gatt_db = NULL;
+ close(fd);
+ queue_destroy(conn_list, NULL);
return;
}

- mainloop_add_fd(att_fd, EPOLLIN, att_conn_callback, NULL, NULL);
+ if (!io_set_read_handler(att_io, att_conn_callback, NULL, NULL))
+ fprintf(stderr, "Failed to add read handler\n");
}

void gatt_server_stop(void)
{
- if (att_fd < 0)
+ if (!att_io)
return;

- mainloop_remove_fd(att_fd);
+ io_destroy(att_io);
+ att_io = NULL;

queue_destroy(conn_list, gatt_conn_destroy);

@@ -313,6 +329,4 @@ void gatt_server_stop(void)
gatt_db_unref(gatt_db);
gatt_db = NULL;

- close(att_fd);
- att_fd = -1;
}
--
2.4.5


2015-07-02 14:33:42

by Vinicius Costa Gomes

[permalink] [raw]
Subject: [RFC BlueZ 01/13] build: Add configure-time checks for soletta

The idea will be to re-use the work done in peripheral/ so we can easily
export node types for common Bluetooth profiles (the focus right now is
GATT profiles).

[1] https://github.com/solettaproject/soletta
---
configure.ac | 38 ++++++++++++++++++++++++++++++++++++++
1 file changed, 38 insertions(+)

diff --git a/configure.ac b/configure.ac
index c65d19a..5c3f832 100644
--- a/configure.ac
+++ b/configure.ac
@@ -283,4 +283,42 @@ fi
AC_DEFINE_UNQUOTED(ANDROID_STORAGEDIR, "${storagedir}/android",
[Directory for the Android daemon storage files])

+AC_ARG_ENABLE(soletta, AC_HELP_STRING([--enable-soletta],
+ [enable building BlueZ soletta nodes]),
+ [enable_soletta=${enableval}])
+AM_CONDITIONAL(SOLETTA, test "${enable_soletta}" = "yes")
+
+if (test "${enable_soletta}" = "yes"); then
+ PKG_CHECK_MODULES(SOLETTA, soletta, dummy=yes,
+ AC_MSG_ERROR(soletta is required))
+ AC_SUBST(SOLETTA_CFLAGS)
+ AC_SUBST(SOLETTA_LIBS)
+ solettaprefix="`$PKG_CONFIG --variable=prefix soletta`"
+ AC_SUBST(SOLETTA_PREFIX, [${solettaprefix}])
+fi
+
+AC_ARG_WITH([solettamodulesdir], AC_HELP_STRING([--with-solettamodulesdir=DIR],
+ [path to soletta modules directory]), [path_solettamodulesdir=${withval}])
+if (test "${enable_soletta}" != "no" && test -z "${path_solettamodulesdir}"); then
+ AC_MSG_CHECKING([soletta modules directory])
+ path_solettamodulesdir="`$PKG_CONFIG --variable=modulesdir soletta`"
+ if (test -z "${path_solettamodulesdir=}"); then
+ AC_MSG_ERROR([soletta modules directory is required])
+ fi
+ AC_MSG_RESULT([${path_solettamodulesdir}])
+fi
+AC_SUBST(SOLETTA_MODULESDIR, [${path_solettamodulesdir}])
+
+AC_ARG_WITH([solettadescdir], AC_HELP_STRING([--with-solettadescdir=DIR],
+ [path to soletta node type descriptions directory]), [path_solettadescdir=${withval}])
+if (test "${enable_soletta}" != "no" && test -z "${path_solettadescdir}"); then
+ AC_MSG_CHECKING([soletta modules directory])
+ path_solettadescdir="`$PKG_CONFIG --variable=pkgdatadir soletta`"
+ if (test -z "${path_solettadescdir=}"); then
+ AC_MSG_ERROR([soletta descriptions directory is required])
+ fi
+ AC_MSG_RESULT([${path_solettadescdir}])
+fi
+AC_SUBST(SOLETTA_DESCDIR, [${path_solettadescdir}])
+
AC_OUTPUT(Makefile src/bluetoothd.8 lib/bluez.pc)
--
2.4.5