summaryrefslogtreecommitdiff
path: root/mesh/mesh.c
diff options
context:
space:
mode:
authorInga Stotland <inga.stotland@intel.com>2018-12-14 14:01:50 -0800
committerBrian Gix <brian.gix@intel.com>2019-01-08 08:26:22 -0800
commit7c11237d9f83124fc53cbdd5aca7dc227f185432 (patch)
treec302ae2d2d53c09784ba358793929f35ca5656b3 /mesh/mesh.c
parent7005bb77bf43ab0d1cb01ad6a30261c1e1d10fee (diff)
downloadbluez-7c11237d9f83124fc53cbdd5aca7dc227f185432.tar.gz
mesh: Implement org.bluez.mesh.Network API
This implements the following methods of org.bluez.mesh.Network interface: Join(), Attach(), Cancel(). The methods are described in doc/mesh-api.txt document. Also, add changes to reflect that the single daemon now handles multiple local mesh nodes.
Diffstat (limited to 'mesh/mesh.c')
-rw-r--r--mesh/mesh.c626
1 files changed, 485 insertions, 141 deletions
diff --git a/mesh/mesh.c b/mesh/mesh.c
index d1d409672..169e6f42c 100644
--- a/mesh/mesh.c
+++ b/mesh/mesh.c
@@ -15,7 +15,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
- *
*/
#ifdef HAVE_CONFIG_H
@@ -23,22 +22,33 @@
#endif
#define _GNU_SOURCE
-#include <time.h>
#include <ell/ell.h>
+#include <json-c/json.h>
#include "lib/bluetooth.h"
#include "lib/mgmt.h"
#include "src/shared/mgmt.h"
-#include "mesh/mesh-defs.h"
#include "mesh/mesh-io.h"
#include "mesh/node.h"
#include "mesh/net.h"
#include "mesh/storage.h"
-#include "mesh/cfgmod.h"
+#include "mesh/provision.h"
#include "mesh/model.h"
+#include "mesh/dbus.h"
+#include "mesh/error.h"
#include "mesh/mesh.h"
+#include "mesh/agent.h"
+
+/*
+ * The default values for mesh configuration. Can be
+ * overwritten by values from mesh.conf
+ */
+#define DEFAULT_PROV_TIMEOUT 60
+#define DEFAULT_ALGORITHMS 0x0001
+
+/* TODO: add more default values */
struct scan_filter {
uint8_t id;
@@ -46,42 +56,49 @@ struct scan_filter {
};
struct bt_mesh {
- struct mesh_net *net;
struct mesh_io *io;
struct l_queue *filters;
- int ref_count;
- uint16_t index;
+ prov_rx_cb_t prov_rx;
+ void *prov_data;
+ uint32_t prov_timeout;
+ uint16_t algorithms;
uint16_t req_index;
uint8_t max_filters;
};
+struct join_data{
+ struct l_dbus_message *msg;
+ struct mesh_agent *agent;
+ const char *sender;
+ const char *app_path;
+ struct mesh_node *node;
+ uint32_t disc_watch;
+ uint8_t uuid[16];
+};
+
+struct attach_data {
+ uint64_t token;
+ struct l_dbus_message *msg;
+ const char *app;
+};
+
+static struct bt_mesh mesh;
static struct l_queue *controllers;
-static struct l_queue *mesh_list;
static struct mgmt *mgmt_mesh;
static bool initialized;
-static struct bt_mesh *current;
+
+/* We allow only one outstanding Join request */
+static struct join_data *join_pending;
+
+/* Pending Attach requests */
+static struct l_queue *attach_queue;
static bool simple_match(const void *a, const void *b)
{
return a == b;
}
-static void save_exit_config(struct bt_mesh *mesh)
-{
- const char *cfg_filename;
-
- if (!mesh_net_cfg_file_get(mesh->net, &cfg_filename) || !cfg_filename)
- return;
-
- /* Preserve the last sequence number before saving configuration */
- storage_local_write_sequence_number(mesh->net,
- mesh_net_get_seq_num(mesh->net));
-
- if (storage_save_config(mesh->net, cfg_filename, true, NULL, NULL))
- l_info("Saved final configuration to %s", cfg_filename);
-}
-
-static void start_io(struct bt_mesh *mesh, uint16_t index)
+static void start_io(uint16_t index)
{
struct mesh_io *io;
struct mesh_io_caps caps;
@@ -91,21 +108,70 @@ static void start_io(struct bt_mesh *mesh, uint16_t index)
io = mesh_io_new(index, MESH_IO_TYPE_GENERIC);
if (!io) {
l_error("Failed to start mesh io (hci %u)", index);
- current = NULL;
return;
}
mesh_io_get_caps(io, &caps);
- mesh->max_filters = caps.max_num_filters;
+ mesh.max_filters = caps.max_num_filters;
+
+ mesh.io = io;
+
+ l_debug("Started mesh (io %p) on hci %u", mesh.io, index);
+
+ node_attach_io(io);
+}
+
+/* Used for any outbound traffic that doesn't have Friendship Constraints */
+/* This includes Beacons, Provisioning and unrestricted Network Traffic */
+bool mesh_send_pkt(uint8_t count, uint16_t interval,
+ uint8_t *data, uint16_t len)
+{
+ struct mesh_io_send_info info = {
+ .type = MESH_IO_TIMING_TYPE_GENERAL,
+ .u.gen.cnt = count,
+ .u.gen.interval = interval,
+ .u.gen.max_delay = 0,
+ .u.gen.min_delay = 0,
+ };
+
+ return mesh_io_send(mesh.io, &info, data, len);
+}
+
+bool mesh_send_cancel(const uint8_t *filter, uint8_t len)
+{
+ return mesh_io_send_cancel(mesh.io, filter, len);
+}
- mesh_net_attach(mesh->net, io);
- mesh_net_set_window_accuracy(mesh->net, caps.window_accuracy);
- mesh->io = io;
- mesh->index = index;
+static void prov_rx(void *user_data, struct mesh_io_recv_info *info,
+ const uint8_t *data, uint16_t len)
+{
+ if (user_data != &mesh)
+ return;
+
+ if (mesh.prov_rx)
+ mesh.prov_rx(mesh.prov_data, data, len);
+}
+
+bool mesh_reg_prov_rx(prov_rx_cb_t cb, void *user_data)
+{
+ if (mesh.prov_rx && mesh.prov_rx != cb)
+ return false;
- current = NULL;
+ mesh.prov_rx = cb;
+ mesh.prov_data = user_data;
- l_debug("Started mesh (io %p) on hci %u", mesh->io, index);
+ return mesh_io_register_recv_cb(mesh.io, MESH_IO_FILTER_PROV,
+ prov_rx, &mesh);
+}
+
+void mesh_unreg_prov_rx(prov_rx_cb_t cb)
+{
+ if (mesh.prov_rx != cb)
+ return;
+
+ mesh.prov_rx = NULL;
+ mesh.prov_data = NULL;
+ mesh_io_deregister_recv_cb(mesh.io, MESH_IO_FILTER_PROV);
}
static void read_info_cb(uint8_t status, uint16_t length,
@@ -115,7 +181,7 @@ static void read_info_cb(uint8_t status, uint16_t length,
const struct mgmt_rp_read_info *rp = param;
uint32_t current_settings, supported_settings;
- if (!current)
+ if (mesh.io)
/* Already initialized */
return;
@@ -148,7 +214,7 @@ static void read_info_cb(uint8_t status, uint16_t length,
return;
}
- start_io(current, index);
+ start_io(index);
}
static void index_added(uint16_t index, uint16_t length, const void *param,
@@ -156,11 +222,8 @@ static void index_added(uint16_t index, uint16_t length, const void *param,
{
l_debug("hci device %u", index);
- if (!current)
- return;
-
- if (current->req_index != MGMT_INDEX_NONE &&
- index != current->req_index) {
+ if (mesh.req_index != MGMT_INDEX_NONE &&
+ index != mesh.req_index) {
l_debug("Ignore index %d", index);
return;
}
@@ -219,145 +282,100 @@ static void read_index_list_cb(uint8_t status, uint16_t length,
}
}
-static bool load_config(struct bt_mesh *mesh, const char *in_config_name)
+static bool init_mgmt(void)
{
- if (!mesh->net)
- return false;
-
- if (!storage_parse_config(mesh->net, in_config_name))
+ mgmt_mesh = mgmt_new_default();
+ if (!mgmt_mesh)
return false;
- /* Register foundational models */
- mesh_config_srv_init(mesh->net, PRIMARY_ELE_IDX);
-
- return true;
-}
-
-static bool init_mesh(void)
-{
- if (initialized)
- return true;
-
controllers = l_queue_new();
if (!controllers)
return false;
- mesh_list = l_queue_new();
- if (!mesh_list)
- return false;
-
- mgmt_mesh = mgmt_new_default();
- if (!mgmt_mesh)
- goto fail;
-
mgmt_register(mgmt_mesh, MGMT_EV_INDEX_ADDED, MGMT_INDEX_NONE,
index_added, NULL, NULL);
mgmt_register(mgmt_mesh, MGMT_EV_INDEX_REMOVED, MGMT_INDEX_NONE,
index_removed, NULL, NULL);
-
- initialized = true;
return true;
-
-fail:
- l_error("Failed to initialize mesh management");
-
- l_queue_destroy(controllers, NULL);
-
- return false;
}
-struct bt_mesh *mesh_new(uint16_t index, const char *config_file)
+bool mesh_init(uint16_t index, const char *config_dir)
{
- struct bt_mesh *mesh;
+ if (initialized)
+ return true;
+
+ if (!init_mgmt()) {
+ l_error("Failed to initialize mesh management");
+ return false;
+ }
- if (!init_mesh())
- return NULL;
+ mesh.req_index = index;
- mesh = l_new(struct bt_mesh, 1);
- if (!mesh)
- return NULL;
+ mesh_model_init();
+ mesh_agent_init();
- mesh->req_index = index;
- mesh->index = MGMT_INDEX_NONE;
+ /* TODO: read mesh.conf */
+ mesh.prov_timeout = DEFAULT_PROV_TIMEOUT;
+ mesh.algorithms = DEFAULT_ALGORITHMS;
- mesh->net = mesh_net_new(index);
- if (!mesh->net) {
- l_free(mesh);
- return NULL;
- }
+ if (!config_dir)
+ config_dir = MESH_STORAGEDIR;
- if (!load_config(mesh, config_file)) {
- l_error("Failed to load mesh configuration: %s", config_file);
- l_free(mesh);
- return NULL;
- }
+ l_info("Loading node configuration from %s", config_dir);
+
+ if (!storage_load_nodes(config_dir))
+ return false;
- /*
- * TODO: Check if another mesh is searching for io.
- * If so, add to pending list and return.
- */
l_debug("send read index_list");
if (mgmt_send(mgmt_mesh, MGMT_OP_READ_INDEX_LIST,
MGMT_INDEX_NONE, 0, NULL,
- read_index_list_cb, mesh, NULL) > 0) {
- current = mesh;
- l_queue_push_tail(mesh_list, mesh);
- return mesh_ref(mesh);
- }
-
- l_free(mesh);
+ read_index_list_cb, NULL, NULL) <= 0)
+ return false;
- return NULL;
+ return true;
}
-struct bt_mesh *mesh_ref(struct bt_mesh *mesh)
+static void attach_exit(void *data)
{
- if (!mesh)
- return NULL;
-
- __sync_fetch_and_add(&mesh->ref_count, 1);
+ struct l_dbus_message *reply;
+ struct attach_data *pending = data;
- return mesh;
+ reply = dbus_error(pending->msg, MESH_ERROR_FAILED, "Failed. Exiting");
+ l_dbus_send(dbus_get_bus(), reply);
+ l_free(pending);
}
-void mesh_unref(struct bt_mesh *mesh)
+void mesh_cleanup(void)
{
- struct mesh_io *io;
+ struct l_dbus_message *reply;
- if (!mesh)
- return;
+ mesh_io_destroy(mesh.io);
+ mgmt_unref(mgmt_mesh);
- if (__sync_sub_and_fetch(&mesh->ref_count, 1))
- return;
+ if (join_pending) {
+ reply = dbus_error(join_pending->msg, MESH_ERROR_FAILED,
+ "Failed. Exiting");
+ l_dbus_send(dbus_get_bus(), reply);
- if (mesh_net_provisioned_get(mesh->net))
- save_exit_config(mesh);
+ if (join_pending->disc_watch)
+ l_dbus_remove_watch(dbus_get_bus(),
+ join_pending->disc_watch);
- node_cleanup(mesh->net);
+ if (join_pending->node)
+ node_free(join_pending->node);
- storage_release(mesh->net);
- io = mesh_net_detach(mesh->net);
- if (io)
- mesh_io_destroy(io);
+ l_free(join_pending);
+ join_pending = NULL;
+ }
- mesh_net_unref(mesh->net);
- l_queue_remove(mesh_list, mesh);
- l_free(mesh);
-}
+ l_queue_destroy(attach_queue, attach_exit);
+ node_cleanup_all();
+ mesh_model_cleanup();
-void mesh_cleanup(void)
-{
l_queue_destroy(controllers, NULL);
- l_queue_destroy(mesh_list, NULL);
- mgmt_unref(mgmt_mesh);
-}
-
-bool mesh_set_output(struct bt_mesh *mesh, const char *config_name)
-{
- if (!config_name)
- return false;
-
- return mesh_net_cfg_file_set(mesh->net, config_name);
+ l_dbus_object_remove_interface(dbus_get_bus(), BLUEZ_MESH_PATH,
+ MESH_NETWORK_INTERFACE);
+ l_dbus_unregister_interface(dbus_get_bus(), MESH_NETWORK_INTERFACE);
}
const char *mesh_status_str(uint8_t err)
@@ -386,7 +404,333 @@ const char *mesh_status_str(uint8_t err)
}
}
-struct mesh_net *mesh_get_net(struct bt_mesh *mesh)
+static void free_pending_join_call(bool failed)
+{
+ if (!join_pending)
+ return;
+
+ if (join_pending->disc_watch)
+ l_dbus_remove_watch(dbus_get_bus(),
+ join_pending->disc_watch);
+
+ mesh_agent_remove(join_pending->agent);
+
+ if (failed) {
+ storage_remove_node_config(join_pending->node);
+ mesh_agent_remove(join_pending->agent);
+ }
+
+ l_free(join_pending);
+ join_pending = NULL;
+}
+
+/* This is being called if the app exits unexpectedly */
+static void prov_disc_cb(struct l_dbus *bus, void *user_data)
+{
+ if (!join_pending)
+ return;
+
+ if (join_pending->msg)
+ l_dbus_message_unref(join_pending->msg);
+
+ acceptor_cancel(&mesh);
+
+ join_pending->disc_watch = 0;
+
+ free_pending_join_call(true);
+}
+
+static const char *prov_status_str(uint8_t status)
+{
+ switch (status) {
+ case PROV_ERR_SUCCESS:
+ return "success";
+ case PROV_ERR_INVALID_PDU:
+ case PROV_ERR_INVALID_FORMAT:
+ case PROV_ERR_UNEXPECTED_PDU:
+ return "bad-pdu";
+ case PROV_ERR_CONFIRM_FAILED:
+ return "confirmation-failed";
+ case PROV_ERR_INSUF_RESOURCE:
+ return "out-of-resources";
+ case PROV_ERR_DECRYPT_FAILED:
+ return "decryption-error";
+ case PROV_ERR_CANT_ASSIGN_ADDR:
+ return "cannot-assign-addresses";
+ case PROV_ERR_TIMEOUT:
+ return "timeout";
+ case PROV_ERR_UNEXPECTED_ERR:
+ default:
+ return "unexpected-error";
+ }
+}
+
+static void send_join_failed(const char *owner, const char *path,
+ uint8_t status)
+{
+ struct l_dbus_message *msg;
+ struct l_dbus *dbus = dbus_get_bus();
+
+ msg = l_dbus_message_new_method_call(dbus, owner, path,
+ MESH_APPLICATION_INTERFACE,
+ "JoinFailed");
+
+ l_dbus_message_set_arguments(msg, "s", prov_status_str(status));
+ l_dbus_send(dbus_get_bus(), msg);
+
+ free_pending_join_call(true);
+}
+
+static bool prov_complete_cb(void *user_data, uint8_t status,
+ struct mesh_prov_node_info *info)
+{
+ struct l_dbus *dbus = dbus_get_bus();
+ struct l_dbus_message *msg;
+ const char *owner;
+ const char *path;
+ const uint8_t *dev_key;
+
+ l_debug("Provisioning complete %s", prov_status_str(status));
+
+ if (!join_pending)
+ return false;
+
+ owner = join_pending->sender;
+ path = join_pending->app_path;
+
+ if (status == PROV_ERR_SUCCESS &&
+ !node_add_pending_local(join_pending->node, info, mesh.io))
+ status = PROV_ERR_UNEXPECTED_ERR;
+
+ if (status != PROV_ERR_SUCCESS) {
+ send_join_failed(owner, path, status);
+ return false;
+ }
+
+ dev_key = node_get_device_key(join_pending->node);
+
+ msg = l_dbus_message_new_method_call(dbus, owner, path,
+ MESH_APPLICATION_INTERFACE,
+ "JoinComplete");
+
+ l_dbus_message_set_arguments(msg, "t", l_get_u64(dev_key));
+
+ l_dbus_send(dbus_get_bus(), msg);
+
+ free_pending_join_call(false);
+
+ return true;
+}
+
+static void node_init_cb(struct mesh_node *node, struct mesh_agent *agent)
+{
+ struct l_dbus_message *reply;
+ uint8_t num_ele;
+
+ if (!node) {
+ reply = dbus_error(join_pending->msg, MESH_ERROR_FAILED,
+ "Failed to create node from application");
+ goto fail;
+ }
+
+ join_pending->node = node;
+ num_ele = node_get_num_elements(node);
+
+ if (!acceptor_start(num_ele, join_pending->uuid, mesh.algorithms,
+ mesh.prov_timeout, agent, prov_complete_cb,
+ &mesh))
+ {
+ reply = dbus_error(join_pending->msg, MESH_ERROR_FAILED,
+ "Failed to start provisioning acceptor");
+ goto fail;
+ }
+
+ reply = l_dbus_message_new_method_return(join_pending->msg);
+ l_dbus_send(dbus_get_bus(), reply);
+ join_pending->msg = NULL;
+
+ return;
+
+fail:
+ l_dbus_send(dbus_get_bus(), reply);
+ mesh_agent_remove(join_pending->agent);
+ l_free(join_pending);
+ join_pending = NULL;
+}
+
+static struct l_dbus_message *join_network_call(struct l_dbus *dbus,
+ struct l_dbus_message *msg,
+ void *user_data)
{
- return mesh->net;
+ const char *app_path, *sender;
+ struct l_dbus_message_iter iter_uuid;
+ uint32_t n;
+
+ l_debug("Join network request");
+
+ if (join_pending)
+ return dbus_error(msg, MESH_ERROR_BUSY,
+ "Provisioning in progress");
+
+ if (!l_dbus_message_get_arguments(msg, "oay", &app_path,
+ &iter_uuid))
+ return dbus_error(msg, MESH_ERROR_INVALID_ARGS, NULL);
+
+ join_pending = l_new(struct join_data, 1);
+
+ l_dbus_message_iter_get_fixed_array(&iter_uuid, join_pending->uuid, &n);
+
+ if (n != 16) {
+ l_free(join_pending);
+ join_pending = NULL;
+ return dbus_error(msg, MESH_ERROR_INVALID_ARGS,
+ "Bad device UUID");
+ }
+
+ sender = l_dbus_message_get_sender(msg);
+
+ join_pending->sender = l_strdup(sender);
+ join_pending->disc_watch = l_dbus_add_disconnect_watch(dbus, sender,
+ prov_disc_cb, NULL, NULL);
+ join_pending->msg = l_dbus_message_ref(msg);
+ join_pending->app_path = app_path;
+
+ /* Try to create a temporary node */
+ node_join(app_path, sender, join_pending->uuid, node_init_cb);
+
+ return NULL;
+}
+
+static struct l_dbus_message *cancel_join_call(struct l_dbus *dbus,
+ struct l_dbus_message *msg,
+ void *user_data)
+{
+ struct l_dbus_message *reply;
+
+ l_debug("Cancel Join");
+
+ if (!join_pending) {
+ reply = dbus_error(msg, MESH_ERROR_DOES_NOT_EXIST,
+ "No join in progress");
+ goto done;
+ }
+
+ acceptor_cancel(&mesh);
+
+ /* Return error to the original Join call */
+ if (join_pending->msg) {
+ reply = dbus_error(join_pending->msg, MESH_ERROR_FAILED, NULL);
+ l_dbus_send(dbus_get_bus(), reply);
+ }
+
+ reply = l_dbus_message_new_method_return(msg);
+ l_dbus_message_set_arguments(reply, "");
+
+ free_pending_join_call(true);
+done:
+ return reply;
+}
+
+static bool match_attach_request(const void *a, const void *b)
+{
+ const struct attach_data *pending = a;
+ const uint64_t *token = b;
+
+ return *token == pending->token;
+}
+
+static void attach_ready_cb(int status, char *node_path, uint64_t token)
+{
+ struct l_dbus_message *reply;
+ struct attach_data *pending;
+
+ pending = l_queue_find(attach_queue, match_attach_request, &token);
+ if (!pending)
+ return;
+
+ if (status != MESH_ERROR_NONE) {
+ const char *desc = (status == MESH_ERROR_NOT_FOUND) ?
+ "Node match not found" : "Attach failed";
+ reply = dbus_error(pending->msg, status, desc);
+ goto done;
+ }
+
+ reply = l_dbus_message_new_method_return(pending->msg);
+
+ node_build_attach_reply(reply, token);
+
+done:
+ l_dbus_send(dbus_get_bus(), reply);
+ l_queue_remove(attach_queue, pending);
+ l_free(pending);
+}
+
+static struct l_dbus_message *attach_call(struct l_dbus *dbus,
+ struct l_dbus_message *msg,
+ void *user_data)
+{
+ uint64_t token = 1;
+ const char *app_path, *sender;
+ struct attach_data *pending;
+
+ l_debug("Attach");
+
+ if (!l_dbus_message_get_arguments(msg, "ot", &app_path, &token))
+ return dbus_error(msg, MESH_ERROR_INVALID_ARGS, NULL);
+
+ sender = l_dbus_message_get_sender(msg);
+
+ if (node_attach(app_path, sender, token, attach_ready_cb) !=
+ MESH_ERROR_NONE)
+ return dbus_error(msg, MESH_ERROR_NOT_FOUND,
+ "Matching node not found");
+
+ pending = l_new(struct attach_data, 1);
+
+ pending->token = token;
+ pending->msg = l_dbus_message_ref(msg);
+
+ if (!attach_queue)
+ attach_queue = l_queue_new();
+
+ l_queue_push_tail(attach_queue, pending);
+
+ return NULL;
+}
+
+static void setup_network_interface(struct l_dbus_interface *iface)
+{
+ l_dbus_interface_method(iface, "Join", 0, join_network_call, "",
+ "oay", "app", "uuid");
+
+ l_dbus_interface_method(iface, "Cancel", 0, cancel_join_call, "", "");
+
+ l_dbus_interface_method(iface, "Attach", 0, attach_call,
+ "oa(ya(qa{sv}))", "ot", "node", "configuration",
+ "app", "token");
+
+ /* TODO: Implement Leave method */
+}
+
+bool mesh_dbus_init(struct l_dbus *dbus)
+{
+ if (!l_dbus_register_interface(dbus, MESH_NETWORK_INTERFACE,
+ setup_network_interface,
+ NULL, false)) {
+ l_info("Unable to register %s interface",
+ MESH_NETWORK_INTERFACE);
+ return false;
+ }
+
+ if (!l_dbus_object_add_interface(dbus, BLUEZ_MESH_PATH,
+ MESH_NETWORK_INTERFACE, NULL)) {
+ l_info("Unable to register the mesh object on '%s'",
+ MESH_NETWORK_INTERFACE);
+ l_dbus_unregister_interface(dbus, MESH_NETWORK_INTERFACE);
+ return false;
+ }
+
+ l_info("Added Network Interface on %s", BLUEZ_MESH_PATH);
+
+ return true;
}