Hi,
Sorry for the delay, Soletta is now using kbuild as a build system,
the format of the configuration files changed and a few other changes,
so it was needed some time for the dust to settle.
Changes from the RFC:
- Added a README;
- Added a sample Makefile for building an binary implementing the
behaviour expressed in the .fbp file;
Cheers,
Vinicius Costa Gomes (15):
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
peripheral: Add a README for the Soletta support
peripheral: Add a Makefile for building a binary using soletta
.gitignore | 5 +
Makefile.am | 8 +-
Makefile.tools | 20 ++
configure.ac | 38 ++++
peripheral/gap.c | 12 +-
peripheral/gatt.c | 124 +++++++++---
peripheral/gatt.h | 6 +
peripheral/soletta/Makefile | 22 +++
peripheral/soletta/README | 140 +++++++++++++
peripheral/soletta/heartrate-genspec.json | 31 +++
peripheral/soletta/heartrate.c | 270 +++++++++++++++++++++++++
peripheral/soletta/heartrate.fbp | 1 +
peripheral/soletta/sol-flow.json | 8 +
src/shared/io-soletta.c | 317 ++++++++++++++++++++++++++++++
src/shared/timeout-soletta.c | 111 +++++++++++
15 files changed, 1078 insertions(+), 35 deletions(-)
create mode 100644 peripheral/soletta/Makefile
create mode 100644 peripheral/soletta/README
create mode 100644 peripheral/soletta/heartrate-genspec.json
create mode 100644 peripheral/soletta/heartrate.c
create mode 100644 peripheral/soletta/heartrate.fbp
create mode 100644 peripheral/soletta/sol-flow.json
create mode 100644 src/shared/io-soletta.c
create mode 100644 src/shared/timeout-soletta.c
--
2.4.6
Add a sample Makefile using some of the soletta tools for building a
binary linking with the soletta binary implementing the Heartrate Sensor
profile.
---
peripheral/soletta/Makefile | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
create mode 100644 peripheral/soletta/Makefile
diff --git a/peripheral/soletta/Makefile b/peripheral/soletta/Makefile
new file mode 100644
index 0000000..86dbd7d
--- /dev/null
+++ b/peripheral/soletta/Makefile
@@ -0,0 +1,22 @@
+PKG_CONFIG ?= pkg-config
+
+PREFIX=$(shell $(PKG_CONFIG) --variable=prefix soletta)
+DATADIR=$(shell $(PKG_CONFIG) --variable=pkgdatadir soletta)
+SOLETTA_CFLAGS=$(shell $(PKG_CONFIG) --cflags soletta)
+SOLETTA_LDFLAGS=$(shell $(PKG_CONFIG) --libs soletta)
+SOLETTA_INCLUDEDIR=$(shell $(PKG_CONFIG) --variable=includedir soletta)
+
+GENERATOR=$(PREFIX)/bin/sol-fbp-generator
+
+all: soletta-init main.c
+
+main.c: sol-flow.json heartrate.json heartrate.fbp
+ $(GENERATOR) -c sol-flow.json -j heartrate.json -j $(DATADIR)/flow/descriptions/timer.json heartrate.fbp $@
+
+soletta-init: main.c
+ $(CC) $(CFLAGS) -Os -o $@ $< .libs/heartrate.a $(SOLETTA_CFLAGS) -include $(SOLETTA_INCLUDEDIR)/soletta/timer-gen.h -include heartrate-gen.h $(SOLETTA_LDFLAGS)
+
+clean:
+ @rm main.c soletta-init
+
+.PHONY: clean
--
2.4.6
This gives an overview about what is Soletta, what is gained by having
this support in the BlueZ tree and how to build and use it.
---
peripheral/soletta/README | 140 ++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 140 insertions(+)
create mode 100644 peripheral/soletta/README
diff --git a/peripheral/soletta/README b/peripheral/soletta/README
new file mode 100644
index 0000000..a11df63
--- /dev/null
+++ b/peripheral/soletta/README
@@ -0,0 +1,140 @@
+Bluetooth node types for Soletta
+********************************
+
+Soletta
+=======
+
+From the Soletta project github[1] repository:
+
+"Soletta Project is a framework for making IoT devices. With Soletta
+Project's libraries developers can easily write software for devices
+that control actuators/sensors and communicate using standard
+technologies. It enables adding smartness even on the smallest edge
+devices."
+
+Soletta support in BlueZ will provide node types for common Bluetooth
+profiles, so applications written for Soletta may target devices
+running BlueZ.
+
+
+Build requirements
+------------------
+
+- GLib
+
+- python-jsonschema (Used for validating JSON files, for code
+ generation and providing meta-data about node types)
+
+- chrpath
+
+
+Building Soletta
+----------------
+
+Soletta uses kconfig as build system, the default config is enough for
+the purposes of building an external module. From the soletta project
+root directory:
+
+$ make alldefconfig
+
+(It will complain if the required dependencies aren't found)
+
+If you run:
+
+$ make menuconfig
+
+Then the configuration can be tweaked for different objectives, for
+example, changing 'Core library -> Target platform' to linux-micro and
+'Core library -> Mainloop' to posix, would make the resulting soletta
+library more adequate for building self contained applications.
+
+After Soletta is configured, the usual incantations for building and
+installing:
+
+$ make
+
+$ make install
+
+
+Bluetooth LE peripheral support for Soletta
+===========================================
+
+LE peripheral use cases mainly target low power devices, potentially
+coin cell operated, so running Linux on these devices is not usually
+adequate for production devices. But when developing applications,
+having all the development/debug tools available for Linux on hand is
+valuable.
+
+What is possible is to develop the application using Soletta node
+nodes on Linux (or linux-micro), and when the application is feature
+complete, target the final device.
+
+
+Building
+--------
+
+Soletta support is activated by the '--enable-soletta' option
+(protected by the --enable-experimental flag) to the configure step
+when building BlueZ. For buildind an static version of the library,
+pass '--enable-static'. For example, from BlueZ root directory:
+
+$ ./bootstrap
+
+$ ./configure --prefix=/usr --enable-experimental --enable-soletta --enable-static
+
+('--enable-static' enables building static version of the libraries as
+well, this makes it easier building a standalone binary, see the last
+section.)
+
+$ make
+
+$ make install
+
+The install step is necessary so the node types (and its associated
+meta-data) are installed where the soletta tools can find them.
+
+
+Running
+-------
+
+After building and installing BlueZ, go to the 'peripheral/soletta'
+directory and do:
+
+$ sol-fbp-runner heartrate.fbp
+
+You may need super user privileges to properly register the Heartrate service.
+
+The content of the 'heartrate.fbp' file should be similar to this:
+
+"""
+Timer(timer:interval=1000) OUT -> IN Heartrate(heartrate)
+"""
+
+It should read like this, create a node of type 'timer' with the
+'interval' option set to '1000' and name it 'Timer', then connect
+'Timer' 'OUT' port to the 'IN' port of the node named 'Heartrate' of
+type 'heartrate'.
+
+
+Standalone binary
+-----------------
+
+Also in the 'peripheral/soletta' there is a sample 'Makefile'
+demonstrating how you would build an executable using the soletta
+tools.
+
+Building an completely static binary is easier accomplished when using
+the musl[2] libc, and it is left as an exercise.
+
+
+Supported Profiles
+==================
+
+Profile name node type name
+------------------------------------------------------
+Heartrate Sensor heartrate
+
+
+[1] https://github.com/solettaproject/soletta
+
+[2] http://www.musl-libc.org/
\ No newline at end of file
--
2.4.6
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.
---
peripheral/soletta/heartrate.fbp | 1 +
peripheral/soletta/sol-flow.json | 8 ++++++++
2 files changed, 9 insertions(+)
create mode 100644 peripheral/soletta/heartrate.fbp
create mode 100644 peripheral/soletta/sol-flow.json
diff --git a/peripheral/soletta/heartrate.fbp b/peripheral/soletta/heartrate.fbp
new file mode 100644
index 0000000..63185c4
--- /dev/null
+++ b/peripheral/soletta/heartrate.fbp
@@ -0,0 +1 @@
+Timer(timer:interval=1000) OUT -> IN Heartrate(heartrate)
diff --git a/peripheral/soletta/sol-flow.json b/peripheral/soletta/sol-flow.json
new file mode 100644
index 0000000..c1f8fca
--- /dev/null
+++ b/peripheral/soletta/sol-flow.json
@@ -0,0 +1,8 @@
+{
+ "nodetypes": [
+ {
+ "name": "Heartrate",
+ "type": "heartrate"
+ }
+ ]
+}
--
2.4.6
---
Makefile.tools | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
diff --git a/Makefile.tools b/Makefile.tools
index 1899fcb..4e5d645 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 = peripheral/soletta/heartrate.json
+solettamodulesdir = @SOLETTA_MODULESDIR@/flow
+solettamodules_LTLIBRARIES = peripheral/soletta/heartrate.la
+peripheral_soletta_heartrate_la_SOURCES = peripheral/soletta/heartrate.c peripheral/gap.h peripheral/gap.c \
+ peripheral/gatt.h peripheral/gatt.c
+peripheral_soletta_heartrate_la_LDFLAGS = $(AM_LDFLAGS) -module -avoid-version
+peripheral_soletta_heartrate_la_LIBADD = src/libshared-soletta.la \
+ lib/libbluetooth-internal.la \
+ @SOLETTA_LIBS@
+peripheral_soletta_heartrate_la_CFLAGS = $(AM_CFLAGS) @SOLETTA_CFLAGS@ -DSOL_FLOW_NODE_TYPE_MODULE_EXTERNAL
+
+peripheral/soletta/%-gen.c peripheral/soletta/%-gen.h: peripheral/soletta/heartrate-genspec.json
+ $(AM_V_GEN)$(PYTHON) @SOLETTA_PREFIX@/bin/sol-flow-node-type-gen.py $< peripheral/soletta/$*-gen.h peripheral/soletta/$*-gen.c peripheral/soletta/heartrate.json
+
+BUILT_SOURCES += peripheral/soletta/heartrate-gen.c peripheral/soletta/heartrate-gen.h
+
+endif
+
tools_3dsp_SOURCES = tools/3dsp.c monitor/bt.h
tools_3dsp_LDADD = src/libshared-mainloop.la
--
2.4.6
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.
---
peripheral/soletta/heartrate-genspec.json | 31 ++++
peripheral/soletta/heartrate.c | 270 ++++++++++++++++++++++++++++++
2 files changed, 301 insertions(+)
create mode 100644 peripheral/soletta/heartrate-genspec.json
create mode 100644 peripheral/soletta/heartrate.c
diff --git a/peripheral/soletta/heartrate-genspec.json b/peripheral/soletta/heartrate-genspec.json
new file mode 100644
index 0000000..2b9c90a
--- /dev/null
+++ b/peripheral/soletta/heartrate-genspec.json
@@ -0,0 +1,31 @@
+{
+ "$schema": "http://solettaproject.github.io/soletta/schemas/node-type-genspec.schema",
+ "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/peripheral/soletta/heartrate.c b/peripheral/soletta/heartrate.c
new file mode 100644
index 0000000..4f6fbfa
--- /dev/null
+++ b/peripheral/soletta/heartrate.c
@@ -0,0 +1,270 @@
+#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 <sol-flow.h>
+#include <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) {
+ 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;
+
+ 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;
+
+ 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];
+
+ 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)
+{
+ 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;
+ }
+
+ hr->disconnect_watch = bt_att_register_disconnect(att, remove_notify_cb, att, NULL);
+
+ 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_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;
+
+ 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;
+
+ gatt_db = gatt_db_ref(db);
+
+ bt_uuid16_create(&uuid, 0x180d);
+
+ service = gatt_db_add_service(gatt_db, &uuid, true, 6);
+
+ /* 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);
+ hr->handle = gatt_db_attribute_get_handle(chr);
+
+ /* 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);
+}
+
+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;
+
+ 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_destroy(hr->to_notify, (queue_destroy_func_t) bt_att_unref);
+
+ 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.6
---
.gitignore | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/.gitignore b/.gitignore
index 7c1b7e0..ac705f5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -154,6 +154,11 @@ android/test-ipc
android/test-*.log
android/test-*.trs
+peripheral/soletta/heartrate-gen.c
+peripheral/soletta/heartrate-gen.h
+peripheral/soletta/heartrate.json
+peripheral/soletta/main.c
+
cscope.in.out
cscope.out
cscope.po.out
--
2.4.6
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.6
---
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.6
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.6
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.6
---
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.6
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.6
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 | 8 +-
src/shared/io-soletta.c | 317 +++++++++++++++++++++++++++++++++++++++++++
src/shared/timeout-soletta.c | 111 +++++++++++++++
3 files changed, 435 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 41ace22..7779c1b 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,12 @@ 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
+src_libshared_soletta_la_CFLAGS = $(AM_CFLAGS) @SOLETTA_CFLAGS@
+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..3ad66f0
--- /dev/null
+++ b/src/shared/io-soletta.c
@@ -0,0 +1,317 @@
+/*
+ *
+ * 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 <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) {
+ io_destroy(io);
+ 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;
+ }
+
+ io_unref(io);
+ return false;
+ }
+
+ 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..f7e2a23
--- /dev/null
+++ b/src/shared/timeout-soletta.c
@@ -0,0 +1,111 @@
+/*
+ *
+ * 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 <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;
+
+ return i + 1;
+}
+
+void timeout_remove(unsigned int id)
+{
+ struct timeout_data *data;
+
+ if (!id)
+ return;
+
+ if ((id - 1) >= MAX_TIMEOUTS)
+ return;
+
+ data = &timeouts[id - 1];
+
+ if (data->destroy)
+ data->destroy(data->user_data);
+
+ sol_timeout_del(data->timeout);
+ data->timeout = NULL;
+}
--
2.4.6
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.6
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 4f81517..9592ffa 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.6