Petri Gynther <pgynther@google.com>
Scott James Remnant <scott@netsplit.com>
Jakub Tyszkowski <jakub.tyszkowski@tieto.com>
-Grzegorz Kołodziejczyk <grzegorz.kolodziejczyk@tieto.com>
+Grzegorz Kołodziejczyk <grzegorz.kolodziejczyk@codecoup.pl>
Marcin Krąglak <marcin.kraglak@tieto.com>
Łukasz Rymanowski <lukasz.rymanowski@codecoup.pl>
Jerzy Kasenberg <jerzy.kasenberg@tieto.com>
+ver 5.48:
+ Fix issue with subscriptions for unpaired devices.
+ Fix issue with handling A2DP and no available SEP.
+ Fix issue with handling AVCTP change path support.
+ Fix issue with handling AVCTP browsing channel.
+ Fix issue with handling AVCTP passthrough PDUs.
+ Fix issue with handling detaching of controller.
+ Fix issue with handling start discovery results.
+ Fix issue with handling non-connectable devices.
+ Fix issue with handling unused parameter in WriteValue.
+ Add support for service side AcquireWrite and AcquireNotify.
+ Add support for providing address type information.
+ Add support for cable based authentication and pairing.
+ Add support for Bluetooth Low-Energy battery service.
+ Add support for BTP client for qualification testing.
+ Add support for additional Mesh control functionality.
+ Mark advertising manager APIs as stable interfaces.
+
+ver 5.47:
+ Fix issue with handling AcquireNotify registration.
+ Fix issue with handling support for reconnection interval.
+ Fix issue with handling A2DP transport and accepting streams.
+ Fix issue with fallback from BR/EDR to LE bearer handling.
+ Add support for appearance and local name advertising data.
+ Add support for retrieving the supported discovery filters.
+ Add support for decoding Bluetooth 5.0 commands and events.
+ Add support for decoding Bluetooth Mesh advertising bearer.
+ Add support for Bluetooth Mesh control application.
+
+ver 5.46:
+ Fix issue with handling ATT over BR/EDR connections.
+ Fix issue with SDP browsing cleanup after connection.
+ Fix issue with pointer dereference and OPP Put request.
+ Fix issue with identity address updates during pairing.
+ Fix issue with not removing services that had disappeared.
+ Add support for improved discovery of included services.
+ Add support for simplified characteristics discovery.
+ Add support for GATT caching configuration option.
+ Add experimental support for AcquireWrite and AcquireNotify.
+
+ver 5.45:
+ Fix issue with agent support in Bluetooth client tool.
+ Fix issue with handling re-connection policy.
+ Fix issue with handling unknown ATT commands.
+ Fix issue with handling GATT Service Includes property.
+ Fix issue with handling PullAll for OBEX transfers.
+ Fix issue with handling delay in AVDTP Suspend responses.
+ Fix issue with handling decoding of management frames.
+ Add support for frame counters in Bluetooth monitor tool.
+
+ver 5.44:
+ Fix issue with GAP and GATT service registration.
+ Fix issue with wrong address type for ATT sockets.
+ Fix issue with dictionary entries for advertising.
+ Fix issue with device information and HID over GATT.
+ Fix issue with handling secondary service discovery.
+ Fix issue with handling Attribute Read Long procedure.
+ Fix issue with handling Attribute Write Long procedure.
+ Fix issue with handling abort of AVDTP SetConfiguration.
+ Add support for single-mode static address configuration.
+ Add support for MIDI over Bluetooth Low Energy.
+
ver 5.43:
Fix issue with HID over GATT support.
Fix issue with ATT Find By Type response handling.
# sudo cp ./src/bluetooth.conf /etc/dbus-1/system.d/
Run daemon in foreground with debugging
- # sudo ./src/bluetoothd -n -d
+ # sudo ./src/bluetoothd -n -d -f ./src/main.conf
Run daemon with valgrind
# sudo valgrind --trace-children=yes --track-origins=yes --track-fds=yes \
--show-possibly-lost=no --leak-check=full --suppressions=./tools/valgrind.supp \
- ./src/bluetoothd -n -d
+ ./src/bluetoothd -n -d -f ./src/main.conf
For production installations or distribution packaging it is important that
the "--enable-maintainer-mode" option is NOT used.
welcome! In order to ease the inclusion of your patch, it's important to follow
some rules, otherwise it will likely be rejected by maintainers.
+Make sure the author name and email are set properly:
+
+ # git config --global user.name <name>
+ # git config --global user.email <email>
+
+The preferred way to send patches is by email, using git send-email:
+
+ # git config --global sendemail.smtpencryption <tls>
+ # git config --global sendemail.smtpserver <smtp.gmail.com>
+ # git config --global sendemail.smtpuser <yourname@gmail.com>
+ # git config --global sendemail.smtpserverport <587>
+ # git config sendemail.to linux-bluetooth@vger.kernel.org
+
BlueZ rules for submitting patches follow most of the rules used by Linux kernel
(https://www.kernel.org/doc/Documentation/SubmittingPatches) with some remarks:
should be limited to 50 characters and the description should be wrapped at 72
characters except if it contains quoted information from debug tools like
backtraces, compiler errors, etc.
+
+6) Prefix the email subject with [PATCH BlueZ]:
+
+ # git config format.subjectprefix "PATCH BlueZ"
+
+7) Add a cover letter when introducing a new feature explaning what problem
+you're trying to solve:
+
+ # git format-patch --cover-letter -M origin/master -o outgoing/
+ # edit outgoing/0000-*
+
+8) Submit:
+
+ # git send-email outgoing/*
lib_LTLIBRARIES += lib/libbluetooth.la
lib_libbluetooth_la_SOURCES = $(lib_headers) $(lib_sources)
-lib_libbluetooth_la_LDFLAGS = $(AM_LDFLAGS) -version-info 21:14:18
+lib_libbluetooth_la_LDFLAGS = $(AM_LDFLAGS) -version-info 21:16:18
lib_libbluetooth_la_DEPENDENCIES = $(local_headers)
endif
src/shared/gatt-server.h src/shared/gatt-server.c \
src/shared/gatt-db.h src/shared/gatt-db.c \
src/shared/gap.h src/shared/gap.c \
- src/shared/tty.h
+ src/shared/tty.h src/shared/shell.c src/shared/shell.h
src_libshared_glib_la_SOURCES = $(shared_sources) \
src/shared/io-glib.c \
builtin_modules =
builtin_sources =
builtin_nodist =
+builtin_ldadd =
include Makefile.plugins
gdbus/libgdbus-internal.la \
src/libshared-glib.la \
@BACKTRACE_LIBS@ @GLIB_LIBS@ @DBUS_LIBS@ @LIBXML_LIBS@ @INIPARSER_LIBS@ -ldl -lrt
+ $(builtin_ldadd)
src_bluetoothd_LDFLAGS = $(AM_LDFLAGS) -Wl,--export-dynamic \
-Wl,--version-script=$(srcdir)/src/bluetooth.ver
endif
TESTS = $(unit_tests)
+AM_TESTS_ENVIRONMENT = MALLOC_CHECK_=3 MALLOC_PERTURB_=69
+
+if DBUS_RUN_SESSION
+AM_TESTS_ENVIRONMENT += dbus-run-session --
+endif
+
+if VALGRIND
+LOG_COMPILER = valgrind --error-exitcode=1 --num-callers=30
+LOG_FLAGS = --trace-children=yes --leak-check=full --show-reachable=no \
+ --suppressions=$(srcdir)/tools/valgrind.supp --quiet
+endif
pkgconfigdir = $(libdir)/pkgconfig
EXTRA_DIST += $(manual_pages:.1=.txt)
DISTCHECK_CONFIGURE_FLAGS = --disable-datafiles --enable-library \
- --enable-manpages \
- --disable-systemd --disable-udev
+ --enable-health \
+ --enable-mesh \
+ --enable-midi \
+ --enable-manpages \
+ --enable-android \
+ --disable-systemd \
+ --disable-udev
DISTCLEANFILES = $(pkgconfig_DATA) $(unit_tests) $(manual_pages)
lib/bluetooth/%.h: lib/%.h
$(AM_V_at)$(MKDIR_P) lib/bluetooth
- $(AM_V_GEN)$(LN_S) -f "$(abs_top_builddir)"/$< $@
+ $(AM_V_GEN)$(LN_S) -f $(abspath $<) $@
if COVERAGE
clean-coverage:
builtin_sources += plugins/policy.c
#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
-if TIZEN_BREDR_PLUGIN
-builtin_modules += dbusoob
-builtin_sources += plugins/dbusoob.c \
+#if TIZEN_BREDR_PLUGIN
+#builtin_modules += dbusoob
+#builtin_sources += plugins/dbusoob.c \
src/oob.h src/oob.c
-endif
+#endif
#endif
if TIZEN_NFC_PLUGIN
builtin_sources += profiles/sap/main.c profiles/sap/manager.h \
profiles/sap/manager.c profiles/sap/server.h \
profiles/sap/server.c profiles/sap/sap.h \
- profiles/sap/sap-u8500.c
+ profiles/sap/sap-dummy.c
-#noinst_LIBRARIES += profiles/sap/libsap.a
-#profiles_sap_libsap_a_SOURCES = profiles/sap/sap.h profiles/sap/sap-u8500.c
+noinst_LIBRARIES += profiles/sap/libsap.a
+profiles_sap_libsap_a_SOURCES = profiles/sap/sap.h profiles/sap/sap-u8500.c
endif
if TIZEN_BREDR_PLUGIN
builtin_sources += profiles/deviceinfo/deviceinfo.c
endif
+if MIDI
+builtin_modules += midi
+builtin_sources += profiles/midi/midi.c \
+ profiles/midi/libmidi.h \
+ profiles/midi/libmidi.c
+builtin_ldadd += @ALSA_LIBS@
+endif
+
if TIZEN_PROXIMITY_PLUGIN
builtin_modules += proximity
builtin_sources += profiles/proximity/main.c profiles/proximity/manager.h \
client/advertising.c \
client/gatt.h client/gatt.c \
monitor/uuid.h monitor/uuid.c
-client_bluetoothctl_LDADD = gdbus/libgdbus-internal.la @GLIB_LIBS@ @DBUS_LIBS@ \
- -lreadline
+client_bluetoothctl_LDADD = gdbus/libgdbus-internal.la src/libshared-glib.la \
+ @GLIB_LIBS@ @DBUS_LIBS@ -lreadline
+endif
+
+if MESH
+bin_PROGRAMS += mesh/meshctl
+
+mesh_meshctl_SOURCES = mesh/main.c \
+ mesh/mesh-net.h \
+ mesh/node.h mesh/node.c \
+ mesh/gatt.h mesh/gatt.c \
+ mesh/crypto.h mesh/crypto.c \
+ mesh/keys.h \
+ mesh/net.h mesh/net.c \
+ mesh/prov.h mesh/prov.c \
+ mesh/util.h mesh/util.c \
+ mesh/agent.h mesh/agent.c \
+ mesh/prov-db.h mesh/prov-db.c \
+ mesh/config-model.h mesh/config-client.c \
+ mesh/config-server.c \
+ mesh/onoff-model.h mesh/onoff-model.c
+mesh_meshctl_LDADD = gdbus/libgdbus-internal.la src/libshared-glib.la \
+ lib/libbluetooth-internal.la \
+ @GLIB_LIBS@ @DBUS_LIBS@ -ljson-c -lreadline
endif
if MONITOR
tools_bluetooth_player_SOURCES = tools/bluetooth-player.c \
client/display.h client/display.c
tools_bluetooth_player_LDADD = gdbus/libgdbus-internal.la \
+ src/libshared-glib.la \
@GLIB_LIBS@ @DBUS_LIBS@ -lreadline
tools_obexctl_SOURCES = tools/obexctl.c \
client/display.h client/display.c
-tools_obexctl_LDADD = gdbus/libgdbus-internal.la \
+tools_obexctl_LDADD = gdbus/libgdbus-internal.la src/libshared-glib.la \
@GLIB_LIBS@ @DBUS_LIBS@ -lreadline
tools_btmgmt_SOURCES = tools/btmgmt.c src/uuid-helper.c client/display.c
The plugin is built into bluetoothd therefore it does not need
to be package separately.
+ --enable-midi
+
+ This option enable MIDI support via ALSA Sequencer.
+
+ By default midi plugin is disabled since it still considered
+ experimental. When bluetoothd will create a new ALSA Sequencer
+ client and port for each device connected that supports the
+ MIDI GATT primary service.
+
+ The plugin is built into bluetoothd therefore it does not need
+ to be package separately.
Information
===========
bluez/src/shared/crypto.c \
bluez/src/shared/uhid.c \
bluez/src/shared/att.c \
+ bluez/src/shared/ad.c \
bluez/src/sdpd-database.c \
bluez/src/sdpd-service.c \
bluez/src/sdpd-request.c \
return avdtp_discover_resp(session, buf, size);
case AVDTP_GET_ALL_CAPABILITIES:
get_all = "ALL_";
+ /* fall through */
case AVDTP_GET_CAPABILITIES:
DBG("GET_%sCAPABILITIES request succeeded", get_all);
if (!avdtp_get_capabilities_resp(session, buf, size))
+++ /dev/null
-/*
- *
- * BlueZ - Bluetooth protocol stack for Linux
- *
- * Copyright (C) 2014 Intel Corporation. All rights reserved.
- *
- *
- * This library is free software; you can rebastribute 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 bastributed 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 <stdbool.h>
-#include <errno.h>
-
-#include <glib.h>
-
-#include "src/log.h"
-
-#include "lib/bluetooth.h"
-#include "lib/sdp.h"
-#include "lib/uuid.h"
-
-#include "src/shared/util.h"
-#include "src/shared/queue.h"
-
-#include "attrib/gattrib.h"
-#include "attrib/att.h"
-#include "attrib/gatt.h"
-
-#include "android/bas.h"
-
-#define ATT_NOTIFICATION_HEADER_SIZE 3
-#define ATT_READ_RESPONSE_HEADER_SIZE 1
-
-struct bt_bas {
- int ref_count;
- GAttrib *attrib;
- struct gatt_primary *primary;
- uint16_t handle;
- uint16_t ccc_handle;
- guint id;
- struct queue *gatt_op;
-};
-
-struct gatt_request {
- unsigned int id;
- struct bt_bas *bas;
- void *user_data;
-};
-
-static void destroy_gatt_req(struct gatt_request *req)
-{
- queue_remove(req->bas->gatt_op, req);
- bt_bas_unref(req->bas);
- free(req);
-}
-
-static void bas_free(struct bt_bas *bas)
-{
- bt_bas_detach(bas);
-
- g_free(bas->primary);
- queue_destroy(bas->gatt_op, (void *) destroy_gatt_req);
- g_free(bas);
-}
-
-struct bt_bas *bt_bas_new(void *primary)
-{
- struct bt_bas *bas;
-
- bas = g_try_new0(struct bt_bas, 1);
- if (!bas)
- return NULL;
-
- bas->gatt_op = queue_new();
- if (!bas->gatt_op) {
- bas_free(bas);
- return NULL;
- }
-
- if (primary)
- bas->primary = g_memdup(primary, sizeof(*bas->primary));
-
- return bt_bas_ref(bas);
-}
-
-struct bt_bas *bt_bas_ref(struct bt_bas *bas)
-{
- if (!bas)
- return NULL;
-
- __sync_fetch_and_add(&bas->ref_count, 1);
-
- return bas;
-}
-
-void bt_bas_unref(struct bt_bas *bas)
-{
- if (!bas)
- return;
-
- if (__sync_sub_and_fetch(&bas->ref_count, 1))
- return;
-
- bas_free(bas);
-}
-
-static struct gatt_request *create_request(struct bt_bas *bas,
- void *user_data)
-{
- struct gatt_request *req;
-
- req = new0(struct gatt_request, 1);
- if (!req)
- return NULL;
-
- req->user_data = user_data;
- req->bas = bt_bas_ref(bas);
-
- return req;
-}
-
-static bool set_and_store_gatt_req(struct bt_bas *bas,
- struct gatt_request *req,
- unsigned int id)
-{
- req->id = id;
- return queue_push_head(bas->gatt_op, req);
-}
-
-static void write_char(struct bt_bas *bas, GAttrib *attrib, uint16_t handle,
- const uint8_t *value, size_t vlen,
- GAttribResultFunc func,
- gpointer user_data)
-{
- struct gatt_request *req;
- unsigned int id;
-
- req = create_request(bas, user_data);
- if (!req)
- return;
-
- id = gatt_write_char(attrib, handle, value, vlen, func, req);
-
- if (set_and_store_gatt_req(bas, req, id))
- return;
-
- error("bas: Could not write characteristic");
- g_attrib_cancel(attrib, id);
- free(req);
-
-}
-
-static void read_char(struct bt_bas *bas, GAttrib *attrib, uint16_t handle,
- GAttribResultFunc func, gpointer user_data)
-{
- struct gatt_request *req;
- unsigned int id;
-
- req = create_request(bas, user_data);
- if (!req)
- return;
-
- id = gatt_read_char(attrib, handle, func, req);
-
- if (set_and_store_gatt_req(bas, req, id))
- return;
-
- error("bas: Could not read characteristic");
- g_attrib_cancel(attrib, id);
- free(req);
-}
-
-static void discover_char(struct bt_bas *bas, GAttrib *attrib,
- uint16_t start, uint16_t end,
- bt_uuid_t *uuid, gatt_cb_t func,
- gpointer user_data)
-{
- struct gatt_request *req;
- unsigned int id;
-
- req = create_request(bas, user_data);
- if (!req)
- return;
-
- id = gatt_discover_char(attrib, start, end, uuid, func, req);
-
- if (set_and_store_gatt_req(bas, req, id))
- return;
-
- error("bas: Could not discover characteristic");
- g_attrib_cancel(attrib, id);
- free(req);
-}
-
-static void discover_desc(struct bt_bas *bas, GAttrib *attrib,
- uint16_t start, uint16_t end, bt_uuid_t *uuid,
- gatt_cb_t func, gpointer user_data)
-{
- struct gatt_request *req;
- unsigned int id;
-
- req = create_request(bas, user_data);
- if (!req)
- return;
-
- id = gatt_discover_desc(attrib, start, end, uuid, func, req);
- if (set_and_store_gatt_req(bas, req, id))
- return;
-
- error("bas: Could not discover descriptor");
- g_attrib_cancel(attrib, id);
- free(req);
-}
-
-static void notification_cb(const guint8 *pdu, guint16 len, gpointer user_data)
-{
- DBG("Battery Level at %u", pdu[ATT_NOTIFICATION_HEADER_SIZE]);
-}
-
-static void read_value_cb(guint8 status, const guint8 *pdu, guint16 len,
- gpointer user_data)
-{
- DBG("Battery Level at %u", pdu[ATT_READ_RESPONSE_HEADER_SIZE]);
-}
-
-static void ccc_written_cb(guint8 status, const guint8 *pdu,
- guint16 plen, gpointer user_data)
-{
- struct gatt_request *req = user_data;
- struct bt_bas *bas = req->user_data;
-
- destroy_gatt_req(req);
-
- if (status != 0) {
- error("Write Scan Refresh CCC failed: %s",
- att_ecode2str(status));
- return;
- }
-
- DBG("Battery Level: notification enabled");
-
- bas->id = g_attrib_register(bas->attrib, ATT_OP_HANDLE_NOTIFY,
- bas->handle, notification_cb, bas,
- NULL);
-}
-
-static void write_ccc(struct bt_bas *bas, GAttrib *attrib, uint16_t handle,
- void *user_data)
-{
- uint8_t value[2];
-
- put_le16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, value);
-
- write_char(bas, attrib, handle, value, sizeof(value), ccc_written_cb,
- user_data);
-}
-
-static void ccc_read_cb(guint8 status, const guint8 *pdu, guint16 len,
- gpointer user_data)
-{
- struct gatt_request *req = user_data;
- struct bt_bas *bas = req->user_data;
-
- destroy_gatt_req(req);
-
- if (status != 0) {
- error("Error reading CCC value: %s", att_ecode2str(status));
- return;
- }
-
- write_ccc(bas, bas->attrib, bas->ccc_handle, bas);
-}
-
-static void discover_descriptor_cb(uint8_t status, GSList *descs,
- void *user_data)
-{
- struct gatt_request *req = user_data;
- struct bt_bas *bas = req->user_data;
- struct gatt_desc *desc;
-
- destroy_gatt_req(req);
-
- if (status != 0) {
- error("Discover descriptors failed: %s", att_ecode2str(status));
- return;
- }
-
- /* There will be only one descriptor on list and it will be CCC */
- desc = descs->data;
- bas->ccc_handle = desc->handle;
-
- read_char(bas, bas->attrib, desc->handle, ccc_read_cb, bas);
-}
-
-static void bas_discovered_cb(uint8_t status, GSList *chars, void *user_data)
-{
- struct gatt_request *req = user_data;
- struct bt_bas *bas = req->user_data;
- struct gatt_char *chr;
- uint16_t start, end;
- bt_uuid_t uuid;
-
- destroy_gatt_req(req);
-
- if (status) {
- error("Battery: %s", att_ecode2str(status));
- return;
- }
-
- chr = chars->data;
- bas->handle = chr->value_handle;
-
- DBG("Battery handle: 0x%04x", bas->handle);
-
- read_char(bas, bas->attrib, bas->handle, read_value_cb, bas);
-
- start = chr->value_handle + 1;
- end = bas->primary->range.end;
-
- bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID);
-
- discover_desc(bas, bas->attrib, start, end, &uuid,
- discover_descriptor_cb, bas);
-}
-
-bool bt_bas_attach(struct bt_bas *bas, void *attrib)
-{
- if (!bas || bas->attrib || !bas->primary)
- return false;
-
- bas->attrib = g_attrib_ref(attrib);
-
- if (bas->handle > 0)
- return true;
-
- discover_char(bas, bas->attrib, bas->primary->range.start,
- bas->primary->range.end, NULL,
- bas_discovered_cb, bas);
-
- return true;
-}
-
-static void cancel_gatt_req(struct gatt_request *req)
-{
- if (g_attrib_cancel(req->bas->attrib, req->id))
- destroy_gatt_req(req);
-}
-
-void bt_bas_detach(struct bt_bas *bas)
-{
- if (!bas || !bas->attrib)
- return;
-
- if (bas->id > 0) {
- g_attrib_unregister(bas->attrib, bas->id);
- bas->id = 0;
- }
-
- queue_foreach(bas->gatt_op, (void *) cancel_gatt_req, NULL);
- g_attrib_unref(bas->attrib);
- bas->attrib = NULL;
-}
#include "src/shared/util.h"
#include "src/shared/mgmt.h"
#include "src/shared/queue.h"
+#include "src/shared/ad.h"
#include "src/eir.h"
#include "lib/sdp.h"
#include "lib/sdp_lib.h"
load_irks(irks);
g_slist_free_full(irks, g_free);
- load_link_keys(keys, cb);
+ if (adapter.supported_settings & MGMT_SETTING_BREDR)
+ load_link_keys(keys, cb);
+ else
+ cb(0, &adapter.bdaddr);
+
g_slist_free_full(keys, g_free);
g_strfreev(devs);
error("Could not clear auto connect list");
}
+static void read_adv_features_complete(uint8_t status, uint16_t length,
+ const void *param, void *user_data)
+{
+ const struct mgmt_rp_read_adv_features *rp = param;
+ bt_bluetooth_ready cb = user_data;
+ int err;
+
+ if (status) {
+ error("Failed to read advertising features for index %u: %s (0x%02x)",
+ adapter.index, mgmt_errstr(status), status);
+ err = -EIO;
+ goto failed;
+ }
+
+ if (length < sizeof(*rp)) {
+ error("Too small read advertising features response");
+ err = -EIO;
+ goto failed;
+ }
+
+ adapter.max_advert_instance = rp->max_instances;
+ info("Max LE advertising instances: %d", adapter.max_advert_instance);
+
+ load_devices_info(cb);
+
+ return;
+
+failed:
+ cb(err, NULL);
+}
+
static void read_info_complete(uint8_t status, uint16_t length,
const void *param, void *user_data)
{
/* Store adapter information */
adapter.dev_class = rp->dev_class[0] | (rp->dev_class[1] << 8) |
- (rp->dev_class[2] << 16);
+ (rp->dev_class[2] << 16);
adapter.supported_settings = le32_to_cpu(rp->supported_settings);
adapter.current_settings = le32_to_cpu(rp->current_settings);
enable_mps();
missing_settings = adapter.current_settings ^
- adapter.supported_settings;
+ adapter.supported_settings;
if (missing_settings & MGMT_SETTING_SSP)
set_mode(MGMT_OP_SET_SSP, 0x01);
if (missing_settings & MGMT_SETTING_BONDABLE)
set_mode(MGMT_OP_SET_BONDABLE, 0x01);
+ if (adapter.supported_settings & MGMT_SETTING_LE) {
+ if (mgmt_send(mgmt_if, MGMT_OP_READ_ADV_FEATURES, adapter.index,
+ 0, NULL,
+ read_adv_features_complete, cb, NULL) == 0) {
+ error("Cannot get LE adv features");
+ err = -EIO;
+ goto failed;
+ }
+ } else {
+ load_devices_info(cb);
+ }
+
load_devices_info(cb);
load_devices_cache();
return false;
}
+struct addrm_adv_user_data {
+ bt_le_addrm_advertising_done cb;
+ void *user_data;
+};
+
+static void add_advertising_cb(uint8_t status, uint16_t length,
+ const void *param, void *user_data)
+{
+ struct addrm_adv_user_data *data = user_data;
+
+ DBG("");
+
+ if (status)
+ error("Failed to add advertising %s (0x%02x))",
+ mgmt_errstr(status), status);
+
+ data->cb(status, data->user_data);
+}
+
+bool bt_le_add_advertising(struct adv_instance *adv,
+ bt_le_addrm_advertising_done cb, void *user_data)
+{
+ struct mgmt_cp_add_advertising *cp;
+ struct addrm_adv_user_data *cb_data;
+ size_t len;
+ size_t adv_data_len = 0;
+ size_t sr_data_len = 0;
+ uint8_t *dst, *adv_data, *sr_data;
+ bool ok = false;
+
+ /* These accept NULL and return NULL */
+ adv_data = bt_ad_generate(adv->ad, &adv_data_len);
+ sr_data = bt_ad_generate(adv->sr, &sr_data_len);
+
+ len = sizeof(*cp) + adv_data_len + sr_data_len;
+ cp = malloc0(len);
+ if (!cp)
+ goto out;
+
+ cp->instance = adv->instance;
+ cp->timeout = adv->timeout;
+ /* XXX: how should we set duration? (kernel defaults to 2s) */
+
+ switch (adv->type) {
+ case ANDROID_ADVERTISING_EVENT_TYPE_CONNECTABLE:
+ cp->flags |= MGMT_ADV_FLAG_CONNECTABLE;
+ break;
+
+ case ANDROID_ADVERTISING_EVENT_TYPE_SCANNABLE:
+ case ANDROID_ADVERTISING_EVENT_TYPE_NON_CONNECTABLE:
+ default:
+ break;
+ }
+
+ if (adv->include_tx_power)
+ cp->flags |= MGMT_ADV_FLAG_TX_POWER;
+
+ dst = cp->data;
+ if (adv_data) {
+ cp->adv_data_len = adv_data_len;
+ memcpy(dst, adv_data, adv_data_len);
+ dst += adv_data_len;
+ }
+
+ if (sr_data) {
+ cp->scan_rsp_len = sr_data_len;
+ memcpy(dst, sr_data, sr_data_len);
+ dst += sr_data_len;
+ }
+
+ DBG("lens: adv=%u sr=%u total=%zu",
+ cp->adv_data_len, cp->scan_rsp_len, len);
+
+ cb_data = new0(typeof(*cb_data), 1);
+ cb_data->cb = cb;
+ cb_data->user_data = user_data;
+
+ ok = (mgmt_send(mgmt_if, MGMT_OP_ADD_ADVERTISING, adapter.index,
+ len, cp, add_advertising_cb, cb_data, free) > 0);
+
+ if (!ok)
+ free(cb_data);
+
+out:
+ free(adv_data);
+ free(sr_data);
+ free(cp);
+
+ return ok;
+}
+
+static void remove_advertising_cb(uint8_t status, uint16_t length,
+ const void *param, void *user_data)
+{
+ struct addrm_adv_user_data *data = user_data;
+
+ DBG("");
+
+ if (status)
+ error("Failed to remove advertising %s (0x%02x))",
+ mgmt_errstr(status), status);
+
+ data->cb(status, data->user_data);
+}
+
+bool bt_le_remove_advertising(struct adv_instance *adv,
+ bt_le_addrm_advertising_done cb, void *user_data)
+{
+ struct mgmt_cp_remove_advertising cp = {
+ .instance = adv->instance,
+ };
+ struct addrm_adv_user_data *cb_data;
+ bool ok;
+
+ cb_data = new0(typeof(*cb_data), 1);
+ cb_data->cb = cb;
+ cb_data->user_data = user_data;
+
+ ok = (mgmt_send(mgmt_if, MGMT_OP_REMOVE_ADVERTISING, adapter.index,
+ sizeof(cp), &cp,
+ remove_advertising_cb, cb_data, free) > 0);
+
+ if (!ok)
+ free(cb_data);
+
+ return ok;
+}
+
bool bt_le_register(bt_le_device_found cb)
{
if (gatt_device_found_cb)
bool bt_paired_register(bt_paired_device_cb cb);
void bt_paired_unregister(bt_paired_device_cb cb);
bool bt_is_pairing(const bdaddr_t *addr);
+
+struct bt_ad;
+struct adv_instance {
+ uint8_t instance;
+ int32_t timeout;
+ int32_t type;
+ struct bt_ad *ad;
+ struct bt_ad *sr;
+ unsigned include_tx_power:1;
+};
+
+/* Values below have no C API definition - only in Java (AdvertiseManager.java)
+ * and bluedroid
+ */
+enum android_adv_type {
+ ANDROID_ADVERTISING_EVENT_TYPE_CONNECTABLE = 0,
+ ANDROID_ADVERTISING_EVENT_TYPE_SCANNABLE = 2,
+ ANDROID_ADVERTISING_EVENT_TYPE_NON_CONNECTABLE = 3,
+};
+
+typedef void (*bt_le_addrm_advertising_done)(uint8_t status, void *user_data);
+bool bt_le_add_advertising(struct adv_instance *adv,
+ bt_le_addrm_advertising_done cb, void *user_data);
+bool bt_le_remove_advertising(struct adv_instance *adv,
+ bt_le_addrm_advertising_done cb, void *user_data);
+++ /dev/null
-/*
- *
- * BlueZ - Bluetooth protocol stack for Linux
- *
- * Copyright (C) 2012 Texas Instruments, Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdbool.h>
-#include <errno.h>
-
-#include <glib.h>
-
-#include "src/log.h"
-
-#include "lib/bluetooth.h"
-#include "lib/sdp.h"
-#include "lib/uuid.h"
-
-#include "src/shared/util.h"
-#include "src/shared/queue.h"
-
-#include "attrib/gattrib.h"
-#include "attrib/att.h"
-#include "attrib/gatt.h"
-
-#include "android/dis.h"
-
-#define PNP_ID_SIZE 7
-
-struct bt_dis {
- int ref_count;
- uint16_t handle;
- uint8_t source;
- uint16_t vendor;
- uint16_t product;
- uint16_t version;
- GAttrib *attrib; /* GATT connection */
- struct gatt_primary *primary; /* Primary details */
- bt_dis_notify notify;
- void *notify_data;
- struct queue *gatt_op;
-};
-
-struct characteristic {
- struct gatt_char attr; /* Characteristic */
- struct bt_dis *d; /* deviceinfo where the char belongs */
-};
-
-struct gatt_request {
- unsigned int id;
- struct bt_dis *dis;
- void *user_data;
-};
-
-static void destroy_gatt_req(struct gatt_request *req)
-{
- queue_remove(req->dis->gatt_op, req);
- bt_dis_unref(req->dis);
- free(req);
-}
-
-static void dis_free(struct bt_dis *dis)
-{
- bt_dis_detach(dis);
-
- g_free(dis->primary);
- queue_destroy(dis->gatt_op, (void *) destroy_gatt_req);
- g_free(dis);
-}
-
-struct bt_dis *bt_dis_new(void *primary)
-{
- struct bt_dis *dis;
-
- dis = g_try_new0(struct bt_dis, 1);
- if (!dis)
- return NULL;
-
- dis->gatt_op = queue_new();
- if (!dis->gatt_op) {
- dis_free(dis);
- return NULL;
- }
-
- if (primary)
- dis->primary = g_memdup(primary, sizeof(*dis->primary));
-
- return bt_dis_ref(dis);
-}
-
-struct bt_dis *bt_dis_ref(struct bt_dis *dis)
-{
- if (!dis)
- return NULL;
-
- __sync_fetch_and_add(&dis->ref_count, 1);
-
- return dis;
-}
-
-void bt_dis_unref(struct bt_dis *dis)
-{
- if (!dis)
- return;
-
- if (__sync_sub_and_fetch(&dis->ref_count, 1))
- return;
-
- dis_free(dis);
-}
-
-static struct gatt_request *create_request(struct bt_dis *dis,
- void *user_data)
-{
- struct gatt_request *req;
-
- req = new0(struct gatt_request, 1);
- if (!req)
- return NULL;
-
- req->user_data = user_data;
- req->dis = bt_dis_ref(dis);
-
- return req;
-}
-
-static bool set_and_store_gatt_req(struct bt_dis *dis,
- struct gatt_request *req,
- unsigned int id)
-{
- req->id = id;
- return queue_push_head(dis->gatt_op, req);
-}
-
-static void read_pnpid_cb(guint8 status, const guint8 *pdu, guint16 len,
- gpointer user_data)
-{
- struct gatt_request *req = user_data;
- struct bt_dis *dis = req->user_data;
- uint8_t value[PNP_ID_SIZE];
- ssize_t vlen;
-
- destroy_gatt_req(req);
-
- if (status != 0) {
- error("Error reading PNP_ID value: %s", att_ecode2str(status));
- return;
- }
-
- vlen = dec_read_resp(pdu, len, value, sizeof(value));
- if (vlen < 0) {
- error("Error reading PNP_ID: Protocol error");
- return;
- }
-
- if (vlen < 7) {
- error("Error reading PNP_ID: Invalid pdu length received");
- return;
- }
-
- dis->source = value[0];
- dis->vendor = get_le16(&value[1]);
- dis->product = get_le16(&value[3]);
- dis->version = get_le16(&value[5]);
-
- DBG("source: 0x%02X vendor: 0x%04X product: 0x%04X version: 0x%04X",
- dis->source, dis->vendor, dis->product, dis->version);
-
- if (dis->notify)
- dis->notify(dis->source, dis->vendor, dis->product,
- dis->version, dis->notify_data);
-}
-
-static void read_char(struct bt_dis *dis, GAttrib *attrib, uint16_t handle,
- GAttribResultFunc func, gpointer user_data)
-{
- struct gatt_request *req;
- unsigned int id;
-
- req = create_request(dis, user_data);
- if (!req)
- return;
-
- id = gatt_read_char(attrib, handle, func, req);
-
- if (set_and_store_gatt_req(dis, req, id))
- return;
-
- error("dis: Could not read characteristic");
- g_attrib_cancel(attrib, id);
- free(req);
-}
-
-static void discover_char(struct bt_dis *dis, GAttrib *attrib,
- uint16_t start, uint16_t end,
- bt_uuid_t *uuid, gatt_cb_t func,
- gpointer user_data)
-{
- struct gatt_request *req;
- unsigned int id;
-
- req = create_request(dis, user_data);
- if (!req)
- return;
-
- id = gatt_discover_char(attrib, start, end, uuid, func, req);
-
- if (set_and_store_gatt_req(dis, req, id))
- return;
-
- error("dis: Could not send discover characteristic");
- g_attrib_cancel(attrib, id);
- free(req);
-}
-
-static void configure_deviceinfo_cb(uint8_t status, GSList *characteristics,
- void *user_data)
-{
- struct gatt_request *req = user_data;
- struct bt_dis *d = req->user_data;
- GSList *l;
-
- destroy_gatt_req(req);
-
- if (status != 0) {
- error("Discover deviceinfo characteristics: %s",
- att_ecode2str(status));
- return;
- }
-
- for (l = characteristics; l; l = l->next) {
- struct gatt_char *c = l->data;
-
- if (strcmp(c->uuid, PNPID_UUID) == 0) {
- d->handle = c->value_handle;
- read_char(d, d->attrib, d->handle, read_pnpid_cb, d);
- break;
- }
- }
-}
-
-bool bt_dis_attach(struct bt_dis *dis, void *attrib)
-{
- struct gatt_primary *primary = dis->primary;
-
- if (dis->attrib || !primary)
- return false;
-
- dis->attrib = g_attrib_ref(attrib);
-
- if (!dis->handle)
- discover_char(dis, dis->attrib, primary->range.start,
- primary->range.end, NULL,
- configure_deviceinfo_cb, dis);
-
- return true;
-}
-
-static void cancel_gatt_req(struct gatt_request *req)
-{
- if (g_attrib_cancel(req->dis->attrib, req->id))
- destroy_gatt_req(req);
-}
-
-void bt_dis_detach(struct bt_dis *dis)
-{
- if (!dis->attrib)
- return;
-
- queue_foreach(dis->gatt_op, (void *) cancel_gatt_req, NULL);
- g_attrib_unref(dis->attrib);
- dis->attrib = NULL;
-}
-
-bool bt_dis_set_notification(struct bt_dis *dis, bt_dis_notify func,
- void *user_data)
-{
- if (!dis)
- return false;
-
- dis->notify = func;
- dis->notify_data = user_data;
-
- return true;
-}
#include "src/shared/queue.h"
#include "src/shared/att.h"
#include "src/shared/gatt-db.h"
+#include "src/shared/ad.h"
#include "attrib/gattrib.h"
#include "attrib/att.h"
#include "attrib/gatt.h"
struct queue *notifications;
gatt_conn_cb_t func;
+
+ struct adv_instance *adv;
};
struct element_id {
static bdaddr_t adapter_addr;
static bool scanning = false;
static unsigned int advertising_cnt = 0;
+static uint32_t adv_inst_bits = 0;
static struct queue *gatt_apps = NULL;
static struct queue *gatt_devices = NULL;
bt_auto_connect_remove(&device->bdaddr);
}
+static void free_adv_instance(struct adv_instance *adv)
+{
+ if (!adv)
+ return;
+
+ if (adv->instance)
+ adv_inst_bits &= ~(1 << (adv->instance - 1));
+
+ bt_ad_unref(adv->ad);
+ bt_ad_unref(adv->sr);
+ free(adv);
+}
+
static void destroy_gatt_app(void *data)
{
struct gatt_app *app = data;
queue_destroy(app->notifications, free);
+ free_adv_instance(app->adv);
+
free(app);
}
HAL_STATUS_UNSUPPORTED);
}
+static struct adv_instance *find_adv_instance(uint32_t client_if)
+{
+ struct gatt_app *app;
+ struct adv_instance *adv;
+ uint8_t inst = 0;
+ unsigned int i;
+
+ app = find_app_by_id(client_if);
+ if (!app)
+ return NULL;
+
+ if (app->adv)
+ return app->adv;
+
+ /* Assume that kernel supports <= 32 advertising instances (5 today)
+ * We have already indicated the number to the android framework layers
+ * via the LE features so we don't check again here.
+ * The kernel will detect the error if needed
+ */
+ for (i = 0; i < sizeof(adv_inst_bits) * 8; i++) {
+ uint32_t mask = 1 << i;
+
+ if (!(adv_inst_bits & mask)) {
+ inst = i + 1;
+ adv_inst_bits |= mask;
+ break;
+ }
+ }
+ if (!inst)
+ return NULL;
+
+ adv = new0(typeof(*adv), 1);
+ adv->instance = inst;
+ app->adv = adv;
+
+ DBG("Assigned advertising instance %d for client %d", inst, client_if);
+
+ return adv;
+};
+
+/* Build advertising data object from a data buffer containing
+ * manufacturer_data, service_data, service uuids (in that order)
+ * The input data is raw with no TLV structure and the service uuids are 128 bit
+ */
+static struct bt_ad *build_adv_data(int32_t manufacturer_data_len,
+ int32_t service_data_len,
+ int32_t service_uuid_len,
+ const uint8_t *data_in)
+{
+ const int one_svc_uuid_len = 128 / 8; /* Android uses 128bit UUIDs */
+ uint8_t *src = (uint8_t *)data_in;
+ struct bt_ad *ad;
+ unsigned num_svc_uuids, i;
+
+ ad = bt_ad_new();
+
+ if (manufacturer_data_len >= 2) { /* Includes manufacturer id */
+ uint16_t manufacturer_id;
+
+ manufacturer_id = bt_get_le16(src);
+ src += 2;
+
+ if (!bt_ad_add_manufacturer_data(ad,
+ manufacturer_id,
+ src,
+ manufacturer_data_len - 2))
+ goto err;
+
+ src += manufacturer_data_len - 2;
+ }
+
+ if (service_data_len >= 2) { /* Includes service uuid (always 16 bit) */
+ bt_uuid_t bt_uuid;
+ uint16_t uuid16;
+
+ uuid16 = bt_get_le16(src);
+ src += 2;
+ bt_uuid16_create(&bt_uuid, uuid16);
+
+ if (!bt_ad_add_service_data(ad,
+ &bt_uuid,
+ src,
+ service_data_len - 2))
+ goto err;
+
+ src += service_data_len - 2;
+ }
+
+ if (service_uuid_len % one_svc_uuid_len) {
+ error("Service UUIDs not multiple of %d bytes (%d)",
+ one_svc_uuid_len, service_uuid_len);
+ num_svc_uuids = 0;
+ } else {
+ num_svc_uuids = service_uuid_len / one_svc_uuid_len;
+ }
+
+ for (i = 0; i < num_svc_uuids; i++) {
+ bt_uuid_t bt_uuid;
+
+ android2uuid(src, &bt_uuid);
+ src += one_svc_uuid_len;
+
+ if (!bt_ad_add_service_uuid(ad, &bt_uuid))
+ goto err;
+ }
+
+ return ad;
+
+err:
+ bt_ad_unref(ad);
+ return NULL;
+}
+
+
static void handle_client_setup_multi_adv(const void *buf, uint16_t len)
{
const struct hal_cmd_gatt_client_setup_multi_adv *cmd = buf;
+ struct hal_ev_gatt_client_multi_adv_enable ev;
+ struct adv_instance *adv;
+ uint8_t status;
- DBG("client_if %d", cmd->client_if);
+ DBG("client_if %d min_interval=%d max_interval=%d type=%d channel_map=0x%x tx_power=%d timeout=%d",
+ cmd->client_if,
+ cmd->min_interval,
+ cmd->max_interval,
+ cmd->type,
+ cmd->channel_map,
+ cmd->tx_power,
+ cmd->timeout);
+
+ adv = find_adv_instance(cmd->client_if);
+ if (!adv) {
+ status = HAL_STATUS_FAILED;
+ goto out;
+ }
- /* TODO */
+ status = HAL_STATUS_SUCCESS;
+ adv->timeout = cmd->timeout;
+ adv->type = cmd->type;
+ if (adv->type != ANDROID_ADVERTISING_EVENT_TYPE_SCANNABLE) {
+ bt_ad_unref(adv->sr);
+ adv->sr = NULL;
+ }
+out:
ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
HAL_OP_GATT_CLIENT_SETUP_MULTI_ADV,
- HAL_STATUS_UNSUPPORTED);
+ status);
+
+ ev.client_if = cmd->client_if;
+ ev.status = status;
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_EV_GATT_CLIENT_MULTI_ADV_ENABLE, sizeof(ev), &ev);
}
+/* This is not currently called by Android 5.1 */
static void handle_client_update_multi_adv(const void *buf, uint16_t len)
{
const struct hal_cmd_gatt_client_update_multi_adv *cmd = buf;
HAL_STATUS_UNSUPPORTED);
}
+struct addrm_adv_cb_data {
+ int32_t client_if;
+ struct adv_instance *adv;
+};
+
+static void add_advertising_cb(uint8_t status, void *user_data)
+{
+ struct addrm_adv_cb_data *cb_data = user_data;
+ struct hal_ev_gatt_client_multi_adv_data ev = {
+ .status = status,
+ .client_if = cb_data->client_if,
+ };
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_EV_GATT_CLIENT_MULTI_ADV_DATA,
+ sizeof(ev), &ev);
+
+ free(cb_data);
+}
+
static void handle_client_setup_multi_adv_inst(const void *buf, uint16_t len)
{
const struct hal_cmd_gatt_client_setup_multi_adv_inst *cmd = buf;
+ struct adv_instance *adv;
+ struct bt_ad *adv_data;
+ struct addrm_adv_cb_data *cb_data = NULL;
+ uint8_t status = HAL_STATUS_FAILED;
+
+ DBG("client_if %d set_scan_rsp=%d include_name=%d include_tx_power=%d appearance=%d manuf_data_len=%d svc_data_len=%d svc_uuid_len=%d",
+ cmd->client_if,
+ cmd->set_scan_rsp,
+ cmd->include_name,
+ cmd->include_tx_power,
+ cmd->appearance,
+ cmd->manufacturer_data_len,
+ cmd->service_data_len,
+ cmd->service_uuid_len
+ );
+
+ adv = find_adv_instance(cmd->client_if);
+ if (!adv)
+ goto out;
+
+ adv->include_tx_power = cmd->include_tx_power ? 1 : 0;
+
+ adv_data = build_adv_data(cmd->manufacturer_data_len,
+ cmd->service_data_len,
+ cmd->service_uuid_len,
+ cmd->data_service_uuid);
+ if (!adv_data)
+ goto out;
+
+ if (cmd->set_scan_rsp) {
+ bt_ad_unref(adv->sr);
+ adv->sr = adv_data;
+ } else {
+ bt_ad_unref(adv->ad);
+ adv->ad = adv_data;
+ }
- DBG("client_if %d", cmd->client_if);
+ cb_data = new0(typeof(*cb_data), 1);
+ cb_data->client_if = cmd->client_if;
+ cb_data->adv = adv;
- /* TODO */
+ if (!bt_le_add_advertising(adv, add_advertising_cb, cb_data)) {
+ error("gatt: Could not add advertising");
+ free(cb_data);
+ goto out;
+ }
+ status = HAL_STATUS_SUCCESS;
+
+out:
ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
- HAL_OP_GATT_CLIENT_SETUP_MULTI_ADV_INST,
- HAL_STATUS_UNSUPPORTED);
+ HAL_OP_GATT_CLIENT_SETUP_MULTI_ADV_INST,
+ status);
+
+ if (status != HAL_STATUS_SUCCESS) {
+ struct hal_ev_gatt_client_multi_adv_data ev = {
+ .status = status,
+ .client_if = cmd->client_if,
+ };
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_EV_GATT_CLIENT_MULTI_ADV_DATA,
+ sizeof(ev), &ev);
+ }
+}
+
+static void remove_advertising_cb(uint8_t status, void *user_data)
+{
+ struct addrm_adv_cb_data *cb_data = user_data;
+ struct hal_ev_gatt_client_multi_adv_data ev = {
+ .status = status,
+ .client_if = cb_data->client_if,
+ };
+
+ free_adv_instance(cb_data->adv);
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_EV_GATT_CLIENT_MULTI_ADV_DISABLE,
+ sizeof(ev), &ev);
+
+ free(cb_data);
}
static void handle_client_disable_multi_adv_inst(const void *buf, uint16_t len)
{
const struct hal_cmd_gatt_client_disable_multi_adv_inst *cmd = buf;
+ struct adv_instance *adv;
+ struct gatt_app *app;
+ struct addrm_adv_cb_data *cb_data = NULL;
+ uint8_t status = HAL_STATUS_FAILED;
DBG("client_if %d", cmd->client_if);
- /* TODO */
+ adv = find_adv_instance(cmd->client_if);
+ if (!adv)
+ goto out;
+
+ cb_data = new0(typeof(*cb_data), 1);
+ cb_data->client_if = cmd->client_if;
+ cb_data->adv = adv;
+
+ if (!bt_le_remove_advertising(adv, remove_advertising_cb, cb_data)) {
+ error("gatt: Could not remove advertising");
+ free(cb_data);
+ goto out;
+ }
+
+ app = find_app_by_id(cmd->client_if);
+ if (app)
+ app->adv = NULL;
+ status = HAL_STATUS_SUCCESS;
+
+out:
ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
- HAL_OP_GATT_CLIENT_DISABLE_MULTI_ADV_INST,
- HAL_STATUS_UNSUPPORTED);
+ HAL_OP_GATT_CLIENT_DISABLE_MULTI_ADV_INST,
+ status);
+
+ if (status != HAL_STATUS_SUCCESS) {
+ struct hal_ev_gatt_client_multi_adv_data ev = {
+ .status = status,
+ .client_if = cmd->client_if,
+ };
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_EV_GATT_CLIENT_MULTI_ADV_DISABLE,
+ sizeof(ev), &ev);
+ }
}
static void handle_client_configure_batchscan(const void *buf, uint16_t len)
{ handle_client_update_multi_adv, false,
sizeof(struct hal_cmd_gatt_client_update_multi_adv) },
/* HAL_OP_GATT_CLIENT_SETUP_MULTI_ADV_INST */
- { handle_client_setup_multi_adv_inst, false,
+ { handle_client_setup_multi_adv_inst, true,
sizeof(struct hal_cmd_gatt_client_setup_multi_adv_inst) },
/* HAL_OP_GATT_CLIENT_DISABLE_MULTI_ADV_INST */
{ handle_client_disable_multi_adv_inst, false,
#include "lib/bluetooth.h"
#include "lib/sdp.h"
#include "lib/sdp_lib.h"
+#include "lib/uuid.h"
#include "src/shared/mgmt.h"
#include "src/shared/util.h"
#include "src/shared/uhid.h"
+#include "src/shared/queue.h"
+#include "src/shared/att.h"
+#include "src/shared/gatt-db.h"
#include "src/sdp-client.h"
#include "src/uuid-helper.h"
#include "src/log.h"
+++ /dev/null
-/*
- *
- * BlueZ - Bluetooth protocol stack for Linux
- *
- * Copyright (C) 2014 Intel Corporation.
- * Copyright (C) 2012 Marcel Holtmann <marcel@holtmann.org>
- * Copyright (C) 2012 Nordic Semiconductor Inc.
- * Copyright (C) 2012 Instituto Nokia de Tecnologia - INdT
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdlib.h>
-#include <stdbool.h>
-#include <errno.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-
-#include <glib.h>
-
-#include "lib/bluetooth.h"
-#include "lib/sdp.h"
-#include "lib/uuid.h"
-
-#include "src/shared/util.h"
-#include "src/shared/uhid.h"
-#include "src/shared/queue.h"
-#include "src/log.h"
-
-#include "attrib/att.h"
-#include "attrib/gattrib.h"
-#include "attrib/gatt.h"
-
-#include "btio/btio.h"
-
-#include "android/scpp.h"
-#include "android/dis.h"
-#include "android/bas.h"
-#include "android/hog.h"
-
-#define HOG_UUID "00001812-0000-1000-8000-00805f9b34fb"
-
-#define HOG_INFO_UUID 0x2A4A
-#define HOG_REPORT_MAP_UUID 0x2A4B
-#define HOG_REPORT_UUID 0x2A4D
-#define HOG_PROTO_MODE_UUID 0x2A4E
-#define HOG_CONTROL_POINT_UUID 0x2A4C
-
-#define HOG_REPORT_TYPE_INPUT 1
-#define HOG_REPORT_TYPE_OUTPUT 2
-#define HOG_REPORT_TYPE_FEATURE 3
-
-#define HOG_PROTO_MODE_BOOT 0
-#define HOG_PROTO_MODE_REPORT 1
-
-#define HOG_REPORT_MAP_MAX_SIZE 512
-#define HID_INFO_SIZE 4
-#define ATT_NOTIFICATION_HEADER_SIZE 3
-
-struct bt_hog {
- int ref_count;
- char *name;
- uint16_t vendor;
- uint16_t product;
- uint16_t version;
- struct gatt_primary *primary;
- GAttrib *attrib;
- GSList *reports;
- struct bt_uhid *uhid;
- int uhid_fd;
- gboolean has_report_id;
- uint16_t bcdhid;
- uint8_t bcountrycode;
- uint16_t proto_mode_handle;
- uint16_t ctrlpt_handle;
- uint8_t flags;
- unsigned int getrep_att;
- uint16_t getrep_id;
- unsigned int setrep_att;
- uint16_t setrep_id;
- struct bt_scpp *scpp;
- struct bt_dis *dis;
- struct queue *bas;
- GSList *instances;
- struct queue *gatt_op;
-};
-
-struct report {
- struct bt_hog *hog;
- uint8_t id;
- uint8_t type;
- uint16_t ccc_handle;
- guint notifyid;
- struct gatt_char *decl;
- uint16_t len;
- uint8_t *value;
-};
-
-struct gatt_request {
- unsigned int id;
- struct bt_hog *hog;
- void *user_data;
-};
-
-static struct gatt_request *create_request(struct bt_hog *hog,
- void *user_data)
-{
- struct gatt_request *req;
-
- req = new0(struct gatt_request, 1);
- if (!req)
- return NULL;
-
- req->user_data = user_data;
- req->hog = bt_hog_ref(hog);
-
- return req;
-}
-
-static bool set_and_store_gatt_req(struct bt_hog *hog,
- struct gatt_request *req,
- unsigned int id)
-{
- req->id = id;
- return queue_push_head(hog->gatt_op, req);
-}
-
-static void destroy_gatt_req(struct gatt_request *req)
-{
- queue_remove(req->hog->gatt_op, req);
- bt_hog_unref(req->hog);
- free(req);
-}
-
-static void write_char(struct bt_hog *hog, GAttrib *attrib, uint16_t handle,
- const uint8_t *value, size_t vlen,
- GAttribResultFunc func,
- gpointer user_data)
-{
- struct gatt_request *req;
- unsigned int id;
-
- req = create_request(hog, user_data);
- if (!req)
- return;
-
- id = gatt_write_char(attrib, handle, value, vlen, func, req);
-
- if (set_and_store_gatt_req(hog, req, id))
- return;
-
- error("hog: Could not read char");
- g_attrib_cancel(attrib, id);
- free(req);
-}
-
-static void read_char(struct bt_hog *hog, GAttrib *attrib, uint16_t handle,
- GAttribResultFunc func, gpointer user_data)
-{
- struct gatt_request *req;
- unsigned int id;
-
- req = create_request(hog, user_data);
- if (!req)
- return;
-
- id = gatt_read_char(attrib, handle, func, req);
-
- if (set_and_store_gatt_req(hog, req, id))
- return;
-
- error("hog: Could not read char");
- g_attrib_cancel(attrib, id);
- free(req);
-}
-
-static void discover_desc(struct bt_hog *hog, GAttrib *attrib,
- uint16_t start, uint16_t end, gatt_cb_t func,
- gpointer user_data)
-{
- struct gatt_request *req;
- unsigned int id;
-
- req = create_request(hog, user_data);
- if (!req)
- return;
-
- id = gatt_discover_desc(attrib, start, end, NULL, func, req);
- if (set_and_store_gatt_req(hog, req, id))
- return;
-
- error("hog: Could not discover descriptors");
- g_attrib_cancel(attrib, id);
- free(req);
-}
-
-static void discover_char(struct bt_hog *hog, GAttrib *attrib,
- uint16_t start, uint16_t end,
- bt_uuid_t *uuid, gatt_cb_t func,
- gpointer user_data)
-{
- struct gatt_request *req;
- unsigned int id;
-
- req = create_request(hog, user_data);
- if (!req)
- return;
-
- id = gatt_discover_char(attrib, start, end, uuid, func, req);
-
- if (set_and_store_gatt_req(hog, req, id))
- return;
-
- error("hog: Could not discover characteristic");
- g_attrib_cancel(attrib, id);
- free(req);
-}
-
-static void discover_primary(struct bt_hog *hog, GAttrib *attrib,
- bt_uuid_t *uuid, gatt_cb_t func,
- gpointer user_data)
-{
- struct gatt_request *req;
- unsigned int id;
-
- req = create_request(hog, user_data);
- if (!req)
- return;
-
- id = gatt_discover_primary(attrib, uuid, func, req);
-
- if (set_and_store_gatt_req(hog, req, id))
- return;
-
- error("hog: Could not send discover primary");
- g_attrib_cancel(attrib, id);
- free(req);
-}
-
-static void find_included(struct bt_hog *hog, GAttrib *attrib,
- uint16_t start, uint16_t end,
- gatt_cb_t func, gpointer user_data)
-{
- struct gatt_request *req;
- unsigned int id;
-
- req = create_request(hog, user_data);
- if (!req)
- return;
-
- id = gatt_find_included(attrib, start, end, func, req);
-
- if (set_and_store_gatt_req(hog, req, id))
- return;
-
- error("Could not find included");
- g_attrib_cancel(attrib, id);
- free(req);
-}
-
-static void report_value_cb(const guint8 *pdu, guint16 len, gpointer user_data)
-{
- struct report *report = user_data;
- struct bt_hog *hog = report->hog;
- struct uhid_event ev;
- uint8_t *buf;
- int err;
-
- if (len < ATT_NOTIFICATION_HEADER_SIZE) {
- error("Malformed ATT notification");
- return;
- }
-
- pdu += ATT_NOTIFICATION_HEADER_SIZE;
- len -= ATT_NOTIFICATION_HEADER_SIZE;
-
- memset(&ev, 0, sizeof(ev));
- ev.type = UHID_INPUT;
- buf = ev.u.input.data;
-
- if (hog->has_report_id) {
- buf[0] = report->id;
- len = MIN(len, sizeof(ev.u.input.data) - 1);
- memcpy(buf + 1, pdu, len);
- ev.u.input.size = ++len;
- } else {
- len = MIN(len, sizeof(ev.u.input.data));
- memcpy(buf, pdu, len);
- ev.u.input.size = len;
- }
-
- err = bt_uhid_send(hog->uhid, &ev);
- if (err < 0) {
- error("bt_uhid_send: %s (%d)", strerror(-err), -err);
- return;
- }
-
- DBG("HoG report (%u bytes)", ev.u.input.size);
-}
-
-static void report_ccc_written_cb(guint8 status, const guint8 *pdu,
- guint16 plen, gpointer user_data)
-{
- struct gatt_request *req = user_data;
- struct report *report = req->user_data;
- struct bt_hog *hog = report->hog;
-
- destroy_gatt_req(req);
-
- if (status != 0) {
- error("Write report characteristic descriptor failed: %s",
- att_ecode2str(status));
- return;
- }
-
- report->notifyid = g_attrib_register(hog->attrib,
- ATT_OP_HANDLE_NOTIFY,
- report->decl->value_handle,
- report_value_cb, report, NULL);
-
- DBG("Report characteristic descriptor written: notifications enabled");
-}
-
-static void write_ccc(struct bt_hog *hog, GAttrib *attrib, uint16_t handle,
- void *user_data)
-{
- uint8_t value[2];
-
- put_le16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, value);
-
- write_char(hog, attrib, handle, value, sizeof(value),
- report_ccc_written_cb, user_data);
-}
-
-static void ccc_read_cb(guint8 status, const guint8 *pdu, guint16 len,
- gpointer user_data)
-{
- struct gatt_request *req = user_data;
- struct report *report = req->user_data;
-
- destroy_gatt_req(req);
-
- if (status != 0) {
- error("Error reading CCC value: %s", att_ecode2str(status));
- return;
- }
-
- write_ccc(report->hog, report->hog->attrib, report->ccc_handle, report);
-}
-
-static void report_reference_cb(guint8 status, const guint8 *pdu,
- guint16 plen, gpointer user_data)
-{
- struct gatt_request *req = user_data;
- struct report *report = req->user_data;
-
- destroy_gatt_req(req);
-
- if (status != 0) {
- error("Read Report Reference descriptor failed: %s",
- att_ecode2str(status));
- return;
- }
-
- if (plen != 3) {
- error("Malformed ATT read response");
- return;
- }
-
- report->id = pdu[1];
- report->type = pdu[2];
- DBG("Report ID: 0x%02x Report type: 0x%02x", pdu[1], pdu[2]);
-
- /* Enable notifications only for Input Reports */
- if (report->type == HOG_REPORT_TYPE_INPUT)
- read_char(report->hog, report->hog->attrib, report->ccc_handle,
- ccc_read_cb, report);
-}
-
-static void external_report_reference_cb(guint8 status, const guint8 *pdu,
- guint16 plen, gpointer user_data);
-
-static void discover_external_cb(uint8_t status, GSList *descs, void *user_data)
-{
- struct gatt_request *req = user_data;
- struct bt_hog *hog = req->user_data;
-
- destroy_gatt_req(req);
-
- if (status != 0) {
- error("Discover external descriptors failed: %s",
- att_ecode2str(status));
- return;
- }
-
- for ( ; descs; descs = descs->next) {
- struct gatt_desc *desc = descs->data;
-
- read_char(hog, hog->attrib, desc->handle,
- external_report_reference_cb,
- hog);
- }
-}
-
-static void discover_external(struct bt_hog *hog, GAttrib *attrib,
- uint16_t start, uint16_t end,
- gpointer user_data)
-{
- bt_uuid_t uuid;
-
- if (start > end)
- return;
-
- bt_uuid16_create(&uuid, GATT_EXTERNAL_REPORT_REFERENCE);
-
- discover_desc(hog, attrib, start, end, discover_external_cb,
- user_data);
-}
-
-static void discover_report_cb(uint8_t status, GSList *descs, void *user_data)
-{
- struct gatt_request *req = user_data;
- struct report *report = req->user_data;
- struct bt_hog *hog = report->hog;
-
- destroy_gatt_req(req);
-
- if (status != 0) {
- error("Discover report descriptors failed: %s",
- att_ecode2str(status));
- return;
- }
-
- for ( ; descs; descs = descs->next) {
- struct gatt_desc *desc = descs->data;
-
- switch (desc->uuid16) {
- case GATT_CLIENT_CHARAC_CFG_UUID:
- report->ccc_handle = desc->handle;
- break;
- case GATT_REPORT_REFERENCE:
- read_char(hog, hog->attrib, desc->handle,
- report_reference_cb, report);
- break;
- }
- }
-}
-
-static void discover_report(struct bt_hog *hog, GAttrib *attrib,
- uint16_t start, uint16_t end,
- gpointer user_data)
-{
- if (start > end)
- return;
-
- discover_desc(hog, attrib, start, end, discover_report_cb, user_data);
-}
-
-static void report_read_cb(guint8 status, const guint8 *pdu, guint16 len,
- gpointer user_data)
-{
- struct gatt_request *req = user_data;
- struct report *report = req->user_data;
-
- destroy_gatt_req(req);
-
- if (status != 0) {
- error("Error reading Report value: %s", att_ecode2str(status));
- return;
- }
-
- if (report->value)
- g_free(report->value);
-
- report->value = g_memdup(pdu, len);
- report->len = len;
-}
-
-static struct report *report_new(struct bt_hog *hog, struct gatt_char *chr)
-{
- struct report *report;
-
- report = g_new0(struct report, 1);
- report->hog = hog;
- report->decl = g_memdup(chr, sizeof(*chr));
- hog->reports = g_slist_append(hog->reports, report);
-
- read_char(hog, hog->attrib, chr->value_handle, report_read_cb, report);
-
- return report;
-}
-
-static void external_service_char_cb(uint8_t status, GSList *chars,
- void *user_data)
-{
- struct gatt_request *req = user_data;
- struct bt_hog *hog = req->user_data;
- struct gatt_primary *primary = hog->primary;
- struct report *report;
- GSList *l;
-
- destroy_gatt_req(req);
-
- if (status != 0) {
- const char *str = att_ecode2str(status);
- DBG("Discover external service characteristic failed: %s", str);
- return;
- }
-
- for (l = chars; l; l = g_slist_next(l)) {
- struct gatt_char *chr, *next;
- uint16_t start, end;
-
- chr = l->data;
- next = l->next ? l->next->data : NULL;
-
- DBG("0x%04x UUID: %s properties: %02x",
- chr->handle, chr->uuid, chr->properties);
-
- report = report_new(hog, chr);
- start = chr->value_handle + 1;
- end = (next ? next->handle - 1 : primary->range.end);
- discover_report(hog, hog->attrib, start, end, report);
- }
-}
-
-static void external_report_reference_cb(guint8 status, const guint8 *pdu,
- guint16 plen, gpointer user_data)
-{
- struct gatt_request *req = user_data;
- struct bt_hog *hog = req->user_data;
- uint16_t uuid16;
- bt_uuid_t uuid;
-
- destroy_gatt_req(req);
-
- if (status != 0) {
- error("Read External Report Reference descriptor failed: %s",
- att_ecode2str(status));
- return;
- }
-
- if (plen != 3) {
- error("Malformed ATT read response");
- return;
- }
-
- uuid16 = get_le16(&pdu[1]);
- DBG("External report reference read, external report characteristic "
- "UUID: 0x%04x", uuid16);
-
- /* Do not discover if is not a Report */
- if (uuid16 != HOG_REPORT_UUID)
- return;
-
- bt_uuid16_create(&uuid, uuid16);
- discover_char(hog, hog->attrib, 0x0001, 0xffff, &uuid,
- external_service_char_cb, hog);
-}
-
-static int report_cmp(gconstpointer a, gconstpointer b)
-{
- const struct report *ra = a, *rb = b;
-
- /* sort by type first.. */
- if (ra->type != rb->type)
- return ra->type - rb->type;
-
- /* skip id check in case of report id 0 */
- if (!rb->id)
- return 0;
-
- /* ..then by id */
- return ra->id - rb->id;
-}
-
-static struct report *find_report(struct bt_hog *hog, uint8_t type, uint8_t id)
-{
- struct report cmp;
- GSList *l;
-
- cmp.type = type;
- cmp.id = hog->has_report_id ? id : 0;
-
- l = g_slist_find_custom(hog->reports, &cmp, report_cmp);
-
- return l ? l->data : NULL;
-}
-
-static struct report *find_report_by_rtype(struct bt_hog *hog, uint8_t rtype,
- uint8_t id)
-{
- uint8_t type;
-
- switch (rtype) {
- case UHID_FEATURE_REPORT:
- type = HOG_REPORT_TYPE_FEATURE;
- break;
- case UHID_OUTPUT_REPORT:
- type = HOG_REPORT_TYPE_OUTPUT;
- break;
- case UHID_INPUT_REPORT:
- type = HOG_REPORT_TYPE_INPUT;
- break;
- default:
- return NULL;
- }
-
- return find_report(hog, type, id);
-}
-
-static void output_written_cb(guint8 status, const guint8 *pdu,
- guint16 plen, gpointer user_data)
-{
- struct gatt_request *req = user_data;
-
- destroy_gatt_req(req);
-
- if (status != 0) {
- error("Write output report failed: %s", att_ecode2str(status));
- return;
- }
-}
-
-static void forward_report(struct uhid_event *ev, void *user_data)
-{
- struct bt_hog *hog = user_data;
- struct report *report;
- void *data;
- int size;
-
- report = find_report_by_rtype(hog, ev->u.output.rtype,
- ev->u.output.data[0]);
- if (!report)
- return;
-
- data = ev->u.output.data;
- size = ev->u.output.size;
- if (hog->has_report_id && size > 0) {
- data++;
- --size;
- }
-
- DBG("Sending report type %d ID %d to handle 0x%X", report->type,
- report->id, report->decl->value_handle);
-
- if (hog->attrib == NULL)
- return;
-
- if (report->decl->properties & GATT_CHR_PROP_WRITE)
- write_char(hog, hog->attrib, report->decl->value_handle,
- data, size, output_written_cb, hog);
- else if (report->decl->properties & GATT_CHR_PROP_WRITE_WITHOUT_RESP)
- gatt_write_cmd(hog->attrib, report->decl->value_handle,
- data, size, NULL, NULL);
-}
-
-static void get_feature(struct uhid_event *ev, void *user_data)
-{
- struct bt_hog *hog = user_data;
- struct report *report;
- struct uhid_event rsp;
- int err;
-
- memset(&rsp, 0, sizeof(rsp));
- rsp.type = UHID_FEATURE_ANSWER;
- rsp.u.feature_answer.id = ev->u.feature.id;
-
- report = find_report_by_rtype(hog, ev->u.feature.rtype,
- ev->u.feature.rnum);
- if (!report) {
- rsp.u.feature_answer.err = ENOTSUP;
- goto done;
- }
-
- if (!report->value) {
- rsp.u.feature_answer.err = EIO;
- goto done;
- }
-
- rsp.u.feature_answer.size = report->len;
- memcpy(rsp.u.feature_answer.data, report->value, report->len);
-
-done:
- err = bt_uhid_send(hog->uhid, &rsp);
- if (err < 0)
- error("bt_uhid_send: %s", strerror(-err));
-}
-
-static void set_report_cb(guint8 status, const guint8 *pdu,
- guint16 plen, gpointer user_data)
-{
- struct bt_hog *hog = user_data;
- struct uhid_event rsp;
- int err;
-
- hog->setrep_att = 0;
-
- memset(&rsp, 0, sizeof(rsp));
- rsp.type = UHID_SET_REPORT_REPLY;
- rsp.u.set_report_reply.id = hog->setrep_id;
- rsp.u.set_report_reply.err = status;
-
- if (status != 0)
- error("Error setting Report value: %s", att_ecode2str(status));
-
- err = bt_uhid_send(hog->uhid, &rsp);
- if (err < 0)
- error("bt_uhid_send: %s", strerror(-err));
-}
-
-static void set_report(struct uhid_event *ev, void *user_data)
-{
- struct bt_hog *hog = user_data;
- struct report *report;
- void *data;
- int size;
- int err;
-
- /* uhid never sends reqs in parallel; if there's a req, it timed out */
- if (hog->setrep_att) {
- g_attrib_cancel(hog->attrib, hog->setrep_att);
- hog->setrep_att = 0;
- }
-
- hog->setrep_id = ev->u.set_report.id;
-
- report = find_report_by_rtype(hog, ev->u.set_report.rtype,
- ev->u.set_report.rnum);
- if (!report) {
- err = ENOTSUP;
- goto fail;
- }
-
- data = ev->u.set_report.data;
- size = ev->u.set_report.size;
- if (hog->has_report_id && size > 0) {
- data++;
- --size;
- }
-
- DBG("Sending report type %d ID %d to handle 0x%X", report->type,
- report->id, report->decl->value_handle);
-
- if (hog->attrib == NULL)
- return;
-
- hog->setrep_att = gatt_write_char(hog->attrib,
- report->decl->value_handle,
- data, size, set_report_cb,
- hog);
- if (!hog->setrep_att) {
- err = ENOMEM;
- goto fail;
- }
-
- return;
-fail:
- /* cancel the request on failure */
- set_report_cb(err, NULL, 0, hog);
-}
-
-static void get_report_cb(guint8 status, const guint8 *pdu, guint16 len,
- gpointer user_data)
-{
- struct bt_hog *hog = user_data;
- struct uhid_event rsp;
- int err;
-
- hog->getrep_att = 0;
-
- memset(&rsp, 0, sizeof(rsp));
- rsp.type = UHID_GET_REPORT_REPLY;
- rsp.u.get_report_reply.id = hog->getrep_id;
-
- if (status != 0) {
- error("Error reading Report value: %s", att_ecode2str(status));
- goto exit;
- }
-
- if (len == 0) {
- error("Error reading Report, length %d", len);
- status = EIO;
- goto exit;
- }
-
- if (pdu[0] != 0x0b) {
- error("Error reading Report, invalid response: %02x", pdu[0]);
- status = EPROTO;
- goto exit;
- }
-
- --len;
- ++pdu;
- if (hog->has_report_id && len > 0) {
- --len;
- ++pdu;
- }
-
- rsp.u.get_report_reply.size = len;
- memcpy(rsp.u.get_report_reply.data, pdu, len);
-
-exit:
- rsp.u.get_report_reply.err = status;
- err = bt_uhid_send(hog->uhid, &rsp);
- if (err < 0)
- error("bt_uhid_send: %s", strerror(-err));
-}
-
-static void get_report(struct uhid_event *ev, void *user_data)
-{
- struct bt_hog *hog = user_data;
- struct report *report;
- guint8 err;
-
- /* uhid never sends reqs in parallel; if there's a req, it timed out */
- if (hog->getrep_att) {
- g_attrib_cancel(hog->attrib, hog->getrep_att);
- hog->getrep_att = 0;
- }
-
- hog->getrep_id = ev->u.get_report.id;
-
- report = find_report_by_rtype(hog, ev->u.get_report.rtype,
- ev->u.get_report.rnum);
- if (!report) {
- err = ENOTSUP;
- goto fail;
- }
-
- hog->getrep_att = gatt_read_char(hog->attrib,
- report->decl->value_handle,
- get_report_cb, hog);
- if (!hog->getrep_att) {
- err = ENOMEM;
- goto fail;
- }
-
- return;
-
-fail:
- /* cancel the request on failure */
- get_report_cb(err, NULL, 0, hog);
-}
-
-static bool get_descriptor_item_info(uint8_t *buf, ssize_t blen, ssize_t *len,
- bool *is_long)
-{
- if (!blen)
- return false;
-
- *is_long = (buf[0] == 0xfe);
-
- if (*is_long) {
- if (blen < 3)
- return false;
-
- /*
- * long item:
- * byte 0 -> 0xFE
- * byte 1 -> data size
- * byte 2 -> tag
- * + data
- */
-
- *len = buf[1] + 3;
- } else {
- uint8_t b_size;
-
- /*
- * short item:
- * byte 0[1..0] -> data size (=0, 1, 2, 4)
- * byte 0[3..2] -> type
- * byte 0[7..4] -> tag
- * + data
- */
-
- b_size = buf[0] & 0x03;
- *len = (b_size ? 1 << (b_size - 1) : 0) + 1;
- }
-
- /* item length should be no more than input buffer length */
- return *len <= blen;
-}
-
-static char *item2string(char *str, uint8_t *buf, uint8_t len)
-{
- char *p = str;
- int i;
-
- /*
- * Since long item tags are not defined except for vendor ones, we
- * just ensure that short items are printed properly (up to 5 bytes).
- */
- for (i = 0; i < 6 && i < len; i++)
- p += sprintf(p, " %02x", buf[i]);
-
- /*
- * If there are some data left, just add continuation mark to indicate
- * this.
- */
- if (i < len)
- sprintf(p, " ...");
-
- return str;
-}
-
-static void report_map_read_cb(guint8 status, const guint8 *pdu, guint16 plen,
- gpointer user_data)
-{
- struct gatt_request *req = user_data;
- struct bt_hog *hog = req->user_data;
- uint8_t value[HOG_REPORT_MAP_MAX_SIZE];
- struct uhid_event ev;
- ssize_t vlen;
- char itemstr[20]; /* 5x3 (data) + 4 (continuation) + 1 (null) */
- int i, err;
- GError *gerr = NULL;
-
- destroy_gatt_req(req);
-
- if (status != 0) {
- error("Report Map read failed: %s", att_ecode2str(status));
- return;
- }
-
- vlen = dec_read_resp(pdu, plen, value, sizeof(value));
- if (vlen < 0) {
- error("ATT protocol error");
- return;
- }
-
- DBG("Report MAP:");
- for (i = 0; i < vlen;) {
- ssize_t ilen = 0;
- bool long_item = false;
-
- if (get_descriptor_item_info(&value[i], vlen - i, &ilen,
- &long_item)) {
- /* Report ID is short item with prefix 100001xx */
- if (!long_item && (value[i] & 0xfc) == 0x84)
- hog->has_report_id = TRUE;
-
- DBG("\t%s", item2string(itemstr, &value[i], ilen));
-
- i += ilen;
- } else {
- error("Report Map parsing failed at %d", i);
-
- /* Just print remaining items at once and break */
- DBG("\t%s", item2string(itemstr, &value[i], vlen - i));
- break;
- }
- }
-
- /* create uHID device */
- memset(&ev, 0, sizeof(ev));
- ev.type = UHID_CREATE;
-
- bt_io_get(g_attrib_get_channel(hog->attrib), &gerr,
- BT_IO_OPT_SOURCE, ev.u.create.phys,
- BT_IO_OPT_DEST, ev.u.create.uniq,
- BT_IO_OPT_INVALID);
- if (gerr) {
- error("Failed to connection details: %s", gerr->message);
- g_error_free(gerr);
- return;
- }
-
- strcpy((char *) ev.u.create.name, hog->name);
- ev.u.create.vendor = hog->vendor;
- ev.u.create.product = hog->product;
- ev.u.create.version = hog->version;
- ev.u.create.country = hog->bcountrycode;
- ev.u.create.bus = BUS_BLUETOOTH;
- ev.u.create.rd_data = value;
- ev.u.create.rd_size = vlen;
-
- err = bt_uhid_send(hog->uhid, &ev);
- if (err < 0) {
- error("bt_uhid_send: %s", strerror(-err));
- return;
- }
-
- bt_uhid_register(hog->uhid, UHID_OUTPUT, forward_report, hog);
- bt_uhid_register(hog->uhid, UHID_FEATURE, get_feature, hog);
- bt_uhid_register(hog->uhid, UHID_GET_REPORT, get_report, hog);
- bt_uhid_register(hog->uhid, UHID_SET_REPORT, set_report, hog);
-}
-
-static void info_read_cb(guint8 status, const guint8 *pdu, guint16 plen,
- gpointer user_data)
-{
- struct gatt_request *req = user_data;
- struct bt_hog *hog = req->user_data;
- uint8_t value[HID_INFO_SIZE];
- ssize_t vlen;
-
- destroy_gatt_req(req);
-
- if (status != 0) {
- error("HID Information read failed: %s",
- att_ecode2str(status));
- return;
- }
-
- vlen = dec_read_resp(pdu, plen, value, sizeof(value));
- if (vlen != 4) {
- error("ATT protocol error");
- return;
- }
-
- hog->bcdhid = get_le16(&value[0]);
- hog->bcountrycode = value[2];
- hog->flags = value[3];
-
- DBG("bcdHID: 0x%04X bCountryCode: 0x%02X Flags: 0x%02X",
- hog->bcdhid, hog->bcountrycode, hog->flags);
-}
-
-static void proto_mode_read_cb(guint8 status, const guint8 *pdu, guint16 plen,
- gpointer user_data)
-{
- struct gatt_request *req = user_data;
- struct bt_hog *hog = req->user_data;
- uint8_t value;
- ssize_t vlen;
-
- destroy_gatt_req(req);
-
- if (status != 0) {
- error("Protocol Mode characteristic read failed: %s",
- att_ecode2str(status));
- return;
- }
-
- vlen = dec_read_resp(pdu, plen, &value, sizeof(value));
- if (vlen < 0) {
- error("ATT protocol error");
- return;
- }
-
- if (value == HOG_PROTO_MODE_BOOT) {
- uint8_t nval = HOG_PROTO_MODE_REPORT;
-
- DBG("HoG is operating in Boot Procotol Mode");
-
- gatt_write_cmd(hog->attrib, hog->proto_mode_handle, &nval,
- sizeof(nval), NULL, NULL);
- } else if (value == HOG_PROTO_MODE_REPORT)
- DBG("HoG is operating in Report Protocol Mode");
-}
-
-static void char_discovered_cb(uint8_t status, GSList *chars, void *user_data)
-{
- struct gatt_request *req = user_data;
- struct bt_hog *hog = req->user_data;
- struct gatt_primary *primary = hog->primary;
- bt_uuid_t report_uuid, report_map_uuid, info_uuid;
- bt_uuid_t proto_mode_uuid, ctrlpt_uuid;
- struct report *report;
- GSList *l;
- uint16_t info_handle = 0, proto_mode_handle = 0;
-
- destroy_gatt_req(req);
-
- if (status != 0) {
- const char *str = att_ecode2str(status);
- DBG("Discover all characteristics failed: %s", str);
- return;
- }
-
- bt_uuid16_create(&report_uuid, HOG_REPORT_UUID);
- bt_uuid16_create(&report_map_uuid, HOG_REPORT_MAP_UUID);
- bt_uuid16_create(&info_uuid, HOG_INFO_UUID);
- bt_uuid16_create(&proto_mode_uuid, HOG_PROTO_MODE_UUID);
- bt_uuid16_create(&ctrlpt_uuid, HOG_CONTROL_POINT_UUID);
-
- for (l = chars; l; l = g_slist_next(l)) {
- struct gatt_char *chr, *next;
- bt_uuid_t uuid;
- uint16_t start, end;
-
- chr = l->data;
- next = l->next ? l->next->data : NULL;
-
- DBG("0x%04x UUID: %s properties: %02x",
- chr->handle, chr->uuid, chr->properties);
-
- bt_string_to_uuid(&uuid, chr->uuid);
-
- start = chr->value_handle + 1;
- end = (next ? next->handle - 1 : primary->range.end);
-
- if (bt_uuid_cmp(&uuid, &report_uuid) == 0) {
- report = report_new(hog, chr);
- discover_report(hog, hog->attrib, start, end, report);
- } else if (bt_uuid_cmp(&uuid, &report_map_uuid) == 0) {
- read_char(hog, hog->attrib, chr->value_handle,
- report_map_read_cb, hog);
- discover_external(hog, hog->attrib, start, end, hog);
- } else if (bt_uuid_cmp(&uuid, &info_uuid) == 0)
- info_handle = chr->value_handle;
- else if (bt_uuid_cmp(&uuid, &proto_mode_uuid) == 0)
- proto_mode_handle = chr->value_handle;
- else if (bt_uuid_cmp(&uuid, &ctrlpt_uuid) == 0)
- hog->ctrlpt_handle = chr->value_handle;
- }
-
- if (proto_mode_handle) {
- hog->proto_mode_handle = proto_mode_handle;
- read_char(hog, hog->attrib, proto_mode_handle,
- proto_mode_read_cb, hog);
- }
-
- if (info_handle)
- read_char(hog, hog->attrib, info_handle, info_read_cb, hog);
-}
-
-static void report_free(void *data)
-{
- struct report *report = data;
-
- g_free(report->value);
- g_free(report->decl);
- g_free(report);
-}
-
-static void cancel_gatt_req(struct gatt_request *req)
-{
- if (g_attrib_cancel(req->hog->attrib, req->id))
- destroy_gatt_req(req);
-}
-
-static void hog_free(void *data)
-{
- struct bt_hog *hog = data;
-
- bt_hog_detach(hog);
-
- queue_destroy(hog->bas, (void *) bt_bas_unref);
- g_slist_free_full(hog->instances, hog_free);
-
- bt_scpp_unref(hog->scpp);
- bt_dis_unref(hog->dis);
- bt_uhid_unref(hog->uhid);
- g_slist_free_full(hog->reports, report_free);
- g_free(hog->name);
- g_free(hog->primary);
- queue_destroy(hog->gatt_op, (void *) destroy_gatt_req);
- g_free(hog);
-}
-
-struct bt_hog *bt_hog_new_default(const char *name, uint16_t vendor,
- uint16_t product, uint16_t version,
- void *primary)
-{
- return bt_hog_new(-1, name, vendor, product, version, primary);
-}
-
-struct bt_hog *bt_hog_new(int fd, const char *name, uint16_t vendor,
- uint16_t product, uint16_t version,
- void *primary)
-{
- struct bt_hog *hog;
-
- hog = g_try_new0(struct bt_hog, 1);
- if (!hog)
- return NULL;
-
- hog->gatt_op = queue_new();
- hog->bas = queue_new();
-
- if (fd < 0)
- hog->uhid = bt_uhid_new_default();
- else
- hog->uhid = bt_uhid_new(fd);
-
- hog->uhid_fd = fd;
-
- if (!hog->gatt_op || !hog->bas || !hog->uhid) {
- hog_free(hog);
- return NULL;
- }
-
- hog->name = g_strdup(name);
- hog->vendor = vendor;
- hog->product = product;
- hog->version = version;
-
- if (primary)
- hog->primary = g_memdup(primary, sizeof(*hog->primary));
-
- return bt_hog_ref(hog);
-}
-
-struct bt_hog *bt_hog_ref(struct bt_hog *hog)
-{
- if (!hog)
- return NULL;
-
- __sync_fetch_and_add(&hog->ref_count, 1);
-
- return hog;
-}
-
-void bt_hog_unref(struct bt_hog *hog)
-{
- if (!hog)
- return;
-
- if (__sync_sub_and_fetch(&hog->ref_count, 1))
- return;
-
- hog_free(hog);
-}
-
-static void find_included_cb(uint8_t status, GSList *services, void *user_data)
-{
- struct gatt_request *req = user_data;
- GSList *l;
-
- DBG("");
-
- destroy_gatt_req(req);
-
- if (status) {
- const char *str = att_ecode2str(status);
- DBG("Find included failed: %s", str);
- return;
- }
-
- for (l = services; l; l = l->next) {
- struct gatt_included *include = l->data;
-
- DBG("included: handle %x, uuid %s",
- include->handle, include->uuid);
- }
-}
-
-static void hog_attach_scpp(struct bt_hog *hog, struct gatt_primary *primary)
-{
- if (hog->scpp) {
- bt_scpp_attach(hog->scpp, hog->attrib);
- return;
- }
-
- hog->scpp = bt_scpp_new(primary);
- if (hog->scpp)
- bt_scpp_attach(hog->scpp, hog->attrib);
-}
-
-static void dis_notify(uint8_t source, uint16_t vendor, uint16_t product,
- uint16_t version, void *user_data)
-{
- struct bt_hog *hog = user_data;
-
- hog->vendor = vendor;
- hog->product = product;
- hog->version = version;
-}
-
-static void hog_attach_dis(struct bt_hog *hog, struct gatt_primary *primary)
-{
- if (hog->dis) {
- bt_dis_attach(hog->dis, hog->attrib);
- return;
- }
-
- hog->dis = bt_dis_new(primary);
- if (hog->dis) {
- bt_dis_set_notification(hog->dis, dis_notify, hog);
- bt_dis_attach(hog->dis, hog->attrib);
- }
-}
-
-static void hog_attach_bas(struct bt_hog *hog, struct gatt_primary *primary)
-{
- struct bt_bas *instance;
-
- instance = bt_bas_new(primary);
- if (!instance)
- return;
-
- bt_bas_attach(instance, hog->attrib);
- queue_push_head(hog->bas, instance);
-}
-
-static void hog_attach_hog(struct bt_hog *hog, struct gatt_primary *primary)
-{
- struct bt_hog *instance;
-
- if (!hog->primary) {
- hog->primary = g_memdup(primary, sizeof(*primary));
- discover_char(hog, hog->attrib, primary->range.start,
- primary->range.end, NULL,
- char_discovered_cb, hog);
- find_included(hog, hog->attrib, primary->range.start,
- primary->range.end, find_included_cb, hog);
- return;
- }
-
- instance = bt_hog_new(hog->uhid_fd, hog->name, hog->vendor,
- hog->product, hog->version, primary);
- if (!instance)
- return;
-
- find_included(instance, hog->attrib, primary->range.start,
- primary->range.end, find_included_cb, instance);
-
- bt_hog_attach(instance, hog->attrib);
- hog->instances = g_slist_append(hog->instances, instance);
-}
-
-static void primary_cb(uint8_t status, GSList *services, void *user_data)
-{
- struct gatt_request *req = user_data;
- struct bt_hog *hog = req->user_data;
- struct gatt_primary *primary;
- GSList *l;
-
- DBG("");
-
- destroy_gatt_req(req);
-
- if (status) {
- const char *str = att_ecode2str(status);
- DBG("Discover primary failed: %s", str);
- return;
- }
-
- if (!services) {
- DBG("No primary service found");
- return;
- }
-
- for (l = services; l; l = l->next) {
- primary = l->data;
-
- if (strcmp(primary->uuid, SCAN_PARAMETERS_UUID) == 0) {
- hog_attach_scpp(hog, primary);
- continue;
- }
-
- if (strcmp(primary->uuid, DEVICE_INFORMATION_UUID) == 0) {
- hog_attach_dis(hog, primary);
- continue;
- }
-
- if (strcmp(primary->uuid, BATTERY_UUID) == 0) {
- hog_attach_bas(hog, primary);
- continue;
- }
-
- if (strcmp(primary->uuid, HOG_UUID) == 0)
- hog_attach_hog(hog, primary);
- }
-}
-
-bool bt_hog_attach(struct bt_hog *hog, void *gatt)
-{
- struct gatt_primary *primary = hog->primary;
- GSList *l;
-
- if (hog->attrib)
- return false;
-
- hog->attrib = g_attrib_ref(gatt);
-
- if (!primary) {
- discover_primary(hog, hog->attrib, NULL, primary_cb, hog);
- return true;
- }
-
- if (hog->scpp)
- bt_scpp_attach(hog->scpp, gatt);
-
- if (hog->dis)
- bt_dis_attach(hog->dis, gatt);
-
- queue_foreach(hog->bas, (void *) bt_bas_attach, gatt);
-
- for (l = hog->instances; l; l = l->next) {
- struct bt_hog *instance = l->data;
-
- bt_hog_attach(instance, gatt);
- }
-
- if (hog->reports == NULL) {
- discover_char(hog, hog->attrib, primary->range.start,
- primary->range.end, NULL,
- char_discovered_cb, hog);
- return true;
- }
-
- for (l = hog->reports; l; l = l->next) {
- struct report *r = l->data;
-
- r->notifyid = g_attrib_register(hog->attrib,
- ATT_OP_HANDLE_NOTIFY,
- r->decl->value_handle,
- report_value_cb, r, NULL);
- }
-
- return true;
-}
-
-void bt_hog_detach(struct bt_hog *hog)
-{
- GSList *l;
-
- if (!hog->attrib)
- return;
-
- queue_foreach(hog->bas, (void *) bt_bas_detach, NULL);
-
- for (l = hog->instances; l; l = l->next) {
- struct bt_hog *instance = l->data;
-
- bt_hog_detach(instance);
- }
-
- for (l = hog->reports; l; l = l->next) {
- struct report *r = l->data;
-
- if (r->notifyid > 0) {
- g_attrib_unregister(hog->attrib, r->notifyid);
- r->notifyid = 0;
- }
- }
-
- if (hog->scpp)
- bt_scpp_detach(hog->scpp);
-
- if (hog->dis)
- bt_dis_detach(hog->dis);
-
- queue_foreach(hog->gatt_op, (void *) cancel_gatt_req, NULL);
- g_attrib_unref(hog->attrib);
- hog->attrib = NULL;
-}
-
-int bt_hog_set_control_point(struct bt_hog *hog, bool suspend)
-{
- uint8_t value = suspend ? 0x00 : 0x01;
-
- if (hog->attrib == NULL)
- return -ENOTCONN;
-
- if (hog->ctrlpt_handle == 0)
- return -ENOTSUP;
-
- gatt_write_cmd(hog->attrib, hog->ctrlpt_handle, &value,
- sizeof(value), NULL, NULL);
-
- return 0;
-}
-
-int bt_hog_send_report(struct bt_hog *hog, void *data, size_t size, int type)
-{
- struct report *report;
- GSList *l;
-
- if (!hog)
- return -EINVAL;
-
- if (!hog->attrib)
- return -ENOTCONN;
-
- report = find_report(hog, type, 0);
- if (!report)
- return -ENOTSUP;
-
- DBG("hog: Write report, handle 0x%X", report->decl->value_handle);
-
- if (report->decl->properties & GATT_CHR_PROP_WRITE)
- write_char(hog, hog->attrib, report->decl->value_handle,
- data, size, output_written_cb, hog);
-
- if (report->decl->properties & GATT_CHR_PROP_WRITE_WITHOUT_RESP)
- gatt_write_cmd(hog->attrib, report->decl->value_handle,
- data, size, NULL, NULL);
-
- for (l = hog->instances; l; l = l->next) {
- struct bt_hog *instance = l->data;
-
- bt_hog_send_report(instance, data, size, type);
- }
-
- return 0;
-}
+++ /dev/null
-/*
- *
- * BlueZ - Bluetooth protocol stack for Linux
- *
- * Copyright (C) 2012 Nordic Semiconductor Inc.
- * Copyright (C) 2012 Instituto Nokia de Tecnologia - INdT
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdbool.h>
-#include <errno.h>
-
-#include <glib.h>
-
-#include "src/log.h"
-
-#include "lib/bluetooth.h"
-#include "lib/sdp.h"
-#include "lib/uuid.h"
-
-#include "src/shared/util.h"
-#include "src/shared/queue.h"
-
-#include "attrib/att.h"
-#include "attrib/gattrib.h"
-#include "attrib/gatt.h"
-
-#include "android/scpp.h"
-
-#define SCAN_INTERVAL_WIN_UUID 0x2A4F
-#define SCAN_REFRESH_UUID 0x2A31
-
-#define SCAN_INTERVAL 0x0060
-#define SCAN_WINDOW 0x0030
-#define SERVER_REQUIRES_REFRESH 0x00
-
-struct bt_scpp {
- int ref_count;
- GAttrib *attrib;
- struct gatt_primary *primary;
- uint16_t interval;
- uint16_t window;
- uint16_t iwhandle;
- uint16_t refresh_handle;
- guint refresh_cb_id;
- struct queue *gatt_op;
-};
-
-static void discover_char(struct bt_scpp *scpp, GAttrib *attrib,
- uint16_t start, uint16_t end,
- bt_uuid_t *uuid, gatt_cb_t func,
- gpointer user_data)
-{
- unsigned int id;
-
- id = gatt_discover_char(attrib, start, end, uuid, func, user_data);
-
- if (queue_push_head(scpp->gatt_op, UINT_TO_PTR(id)))
- return;
-
- error("scpp: Could not discover characteristic");
- g_attrib_cancel(attrib, id);
-}
-
-static void discover_desc(struct bt_scpp *scpp, GAttrib *attrib,
- uint16_t start, uint16_t end, bt_uuid_t *uuid,
- gatt_cb_t func, gpointer user_data)
-{
- unsigned int id;
-
- id = gatt_discover_desc(attrib, start, end, uuid, func, user_data);
-
- if (queue_push_head(scpp->gatt_op, UINT_TO_PTR(id)))
- return;
-
- error("scpp: Could not discover descriptor");
- g_attrib_cancel(attrib, id);
-}
-
-static void write_char(struct bt_scpp *scan, GAttrib *attrib, uint16_t handle,
- const uint8_t *value, size_t vlen,
- GAttribResultFunc func,
- gpointer user_data)
-{
- unsigned int id;
-
- id = gatt_write_char(attrib, handle, value, vlen, func, user_data);
-
- if (queue_push_head(scan->gatt_op, UINT_TO_PTR(id)))
- return;
-
- error("scpp: Could not read char");
- g_attrib_cancel(attrib, id);
-}
-
-static void scpp_free(struct bt_scpp *scan)
-{
- bt_scpp_detach(scan);
-
- g_free(scan->primary);
- queue_destroy(scan->gatt_op, NULL); /* cleared in bt_scpp_detach */
- g_free(scan);
-}
-
-struct bt_scpp *bt_scpp_new(void *primary)
-{
- struct bt_scpp *scan;
-
- scan = g_try_new0(struct bt_scpp, 1);
- if (!scan)
- return NULL;
-
- scan->interval = SCAN_INTERVAL;
- scan->window = SCAN_WINDOW;
-
- scan->gatt_op = queue_new();
- if (!scan->gatt_op) {
- scpp_free(scan);
- return NULL;
- }
-
- if (primary)
- scan->primary = g_memdup(primary, sizeof(*scan->primary));
-
- return bt_scpp_ref(scan);
-}
-
-struct bt_scpp *bt_scpp_ref(struct bt_scpp *scan)
-{
- if (!scan)
- return NULL;
-
- __sync_fetch_and_add(&scan->ref_count, 1);
-
- return scan;
-}
-
-void bt_scpp_unref(struct bt_scpp *scan)
-{
- if (!scan)
- return;
-
- if (__sync_sub_and_fetch(&scan->ref_count, 1))
- return;
-
- scpp_free(scan);
-}
-
-static void write_scan_params(GAttrib *attrib, uint16_t handle,
- uint16_t interval, uint16_t window)
-{
- uint8_t value[4];
-
- put_le16(interval, &value[0]);
- put_le16(window, &value[2]);
-
- gatt_write_cmd(attrib, handle, value, sizeof(value), NULL, NULL);
-}
-
-static void refresh_value_cb(const uint8_t *pdu, uint16_t len,
- gpointer user_data)
-{
- struct bt_scpp *scan = user_data;
-
- DBG("Server requires refresh: %d", pdu[3]);
-
- if (pdu[3] == SERVER_REQUIRES_REFRESH)
- write_scan_params(scan->attrib, scan->iwhandle, scan->interval,
- scan->window);
-}
-
-static void ccc_written_cb(guint8 status, const guint8 *pdu,
- guint16 plen, gpointer user_data)
-{
- struct bt_scpp *scan = user_data;
-
- if (status != 0) {
- error("Write Scan Refresh CCC failed: %s",
- att_ecode2str(status));
- return;
- }
-
- DBG("Scan Refresh: notification enabled");
-
- scan->refresh_cb_id = g_attrib_register(scan->attrib,
- ATT_OP_HANDLE_NOTIFY, scan->refresh_handle,
- refresh_value_cb, scan, NULL);
-}
-
-static void write_ccc(struct bt_scpp *scan, GAttrib *attrib, uint16_t handle,
- void *user_data)
-{
- uint8_t value[2];
-
- put_le16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, value);
-
- write_char(scan, attrib, handle, value, sizeof(value), ccc_written_cb,
- user_data);
-}
-
-static void discover_descriptor_cb(uint8_t status, GSList *descs,
- void *user_data)
-{
- struct bt_scpp *scan = user_data;
- struct gatt_desc *desc;
-
- if (status != 0) {
- error("Discover descriptors failed: %s", att_ecode2str(status));
- return;
- }
-
- /* There will be only one descriptor on list and it will be CCC */
- desc = descs->data;
-
- write_ccc(scan, scan->attrib, desc->handle, scan);
-}
-
-static void refresh_discovered_cb(uint8_t status, GSList *chars,
- void *user_data)
-{
- struct bt_scpp *scan = user_data;
- struct gatt_char *chr;
- uint16_t start, end;
- bt_uuid_t uuid;
-
- if (status) {
- error("Scan Refresh %s", att_ecode2str(status));
- return;
- }
-
- if (!chars) {
- DBG("Scan Refresh not supported");
- return;
- }
-
- chr = chars->data;
-
- DBG("Scan Refresh handle: 0x%04x", chr->value_handle);
-
- start = chr->value_handle + 1;
- end = scan->primary->range.end;
-
- if (start > end)
- return;
-
- scan->refresh_handle = chr->value_handle;
-
- bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID);
-
- discover_desc(scan, scan->attrib, start, end, &uuid,
- discover_descriptor_cb, user_data);
-}
-
-static void iwin_discovered_cb(uint8_t status, GSList *chars, void *user_data)
-{
- struct bt_scpp *scan = user_data;
- struct gatt_char *chr;
-
- if (status) {
- error("Discover Scan Interval Window: %s",
- att_ecode2str(status));
- return;
- }
-
- chr = chars->data;
- scan->iwhandle = chr->value_handle;
-
- DBG("Scan Interval Window handle: 0x%04x", scan->iwhandle);
-
- write_scan_params(scan->attrib, scan->iwhandle, scan->interval,
- scan->window);
-}
-
-bool bt_scpp_attach(struct bt_scpp *scan, void *attrib)
-{
- bt_uuid_t iwin_uuid, refresh_uuid;
-
- if (!scan || scan->attrib || !scan->primary)
- return false;
-
- scan->attrib = g_attrib_ref(attrib);
-
- if (scan->iwhandle)
- write_scan_params(scan->attrib, scan->iwhandle, scan->interval,
- scan->window);
- else {
- bt_uuid16_create(&iwin_uuid, SCAN_INTERVAL_WIN_UUID);
- discover_char(scan, scan->attrib, scan->primary->range.start,
- scan->primary->range.end, &iwin_uuid,
- iwin_discovered_cb, scan);
- }
-
- if (scan->refresh_handle)
- scan->refresh_cb_id = g_attrib_register(scan->attrib,
- ATT_OP_HANDLE_NOTIFY, scan->refresh_handle,
- refresh_value_cb, scan, NULL);
- else {
- bt_uuid16_create(&refresh_uuid, SCAN_REFRESH_UUID);
- discover_char(scan, scan->attrib, scan->primary->range.start,
- scan->primary->range.end, &refresh_uuid,
- refresh_discovered_cb, scan);
- }
-
- return true;
-}
-
-static void cancel_gatt_req(void *data, void *user_data)
-{
- unsigned int id = PTR_TO_UINT(data);
- struct bt_scpp *scan = user_data;
-
- g_attrib_cancel(scan->attrib, id);
-}
-
-void bt_scpp_detach(struct bt_scpp *scan)
-{
- if (!scan || !scan->attrib)
- return;
-
- if (scan->refresh_cb_id > 0) {
- g_attrib_unregister(scan->attrib, scan->refresh_cb_id);
- scan->refresh_cb_id = 0;
- }
-
- queue_foreach(scan->gatt_op, cancel_gatt_req, scan);
- g_attrib_unref(scan->attrib);
- scan->attrib = NULL;
-}
-
-bool bt_scpp_set_interval(struct bt_scpp *scan, uint16_t value)
-{
- if (!scan)
- return false;
-
- /* TODO: Check valid range */
-
- scan->interval = value;
-
- return true;
-}
-
-bool bt_scpp_set_window(struct bt_scpp *scan, uint16_t value)
-{
- if (!scan)
- return false;
-
- /* TODO: Check valid range */
-
- scan->window = value;
-
- return true;
-}
+++ /dev/null
-/*
- *
- * BlueZ - Bluetooth protocol stack for Linux
- *
- * Copyright (C) 2014 Intel Corporation. All rights reserved.
- *
- *
- * This library is free software; you can rescpptribute 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 scpptributed 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
- *
- */
-
-struct bt_scpp;
-
-struct bt_scpp *bt_scpp_new(void *primary);
-
-struct bt_scpp *bt_scpp_ref(struct bt_scpp *scan);
-void bt_scpp_unref(struct bt_scpp *scan);
-
-bool bt_scpp_attach(struct bt_scpp *scan, void *gatt);
-void bt_scpp_detach(struct bt_scpp *scan);
-
-bool bt_scpp_set_interval(struct bt_scpp *scan, uint16_t value);
-bool bt_scpp_set_window(struct bt_scpp *scan, uint16_t value);
{ BT_PROPERTY_UUIDS, sizeof(emu_uuids_val), &emu_uuids_val },
};
-static bt_property_t prop_emu_remote_bles_default_set[] = {
+static bt_property_t prop_emu_remote_ble_default_set[] = {
{ BT_PROPERTY_BDADDR, sizeof(emu_remote_bdaddr_val),
&emu_remote_bdaddr_val },
{ BT_PROPERTY_TYPE_OF_DEVICE, sizeof(emu_remote_tod_ble_val),
&emu_remote_ble_rssi_val },
};
-static bt_property_t prop_emu_remotes_default_set[] = {
+static bt_property_t prop_emu_remote_bredr_default_set[] = {
{ BT_PROPERTY_BDADDR, sizeof(emu_remote_bdaddr_val),
&emu_remote_bdaddr_val },
{ BT_PROPERTY_TYPE_OF_DEVICE, sizeof(emu_remote_type_val),
&emu_remote_rssi_val },
};
+static bt_property_t prop_emu_remote_any_default_set[] = {
+ { BT_PROPERTY_BDADDR, sizeof(emu_remote_bdaddr_val),
+ &emu_remote_bdaddr_val },
+};
+
static bt_property_t prop_emu_remote_bles_query_set[] = {
{ BT_PROPERTY_TYPE_OF_DEVICE, sizeof(emu_remote_tod_ble_val),
&emu_remote_tod_ble_val },
TEST_CASE_BREDRLE("Bluetooth Discovery Device Found",
ACTION_SUCCESS(bluetooth_enable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
- ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
ACTION_SUCCESS(bt_start_discovery_action, NULL),
CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
BT_DISCOVERY_STARTED),
- CALLBACK_DEVICE_FOUND(prop_emu_remote_bles_default_set, 3),
+ ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
+ CALLBACK_DEVICE_FOUND(prop_emu_remote_ble_default_set, 3),
ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
BT_DISCOVERY_STOPPED),
TEST_CASE_BREDRLE("Bluetooth Device Get Props - Success",
ACTION_SUCCESS(bluetooth_enable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
- ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
ACTION_SUCCESS(bt_start_discovery_action, NULL),
CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
BT_DISCOVERY_STARTED),
+ ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
+ CALLBACK_DEVICE_FOUND(prop_emu_remote_any_default_set, 1),
ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
BT_DISCOVERY_STOPPED),
TEST_CASE_BREDRLE("Bluetooth Device Get BDNAME - Success",
ACTION_SUCCESS(bluetooth_enable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
- ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
ACTION_SUCCESS(bt_start_discovery_action, NULL),
CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
BT_DISCOVERY_STARTED),
+ ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
+ CALLBACK_DEVICE_FOUND(prop_emu_remote_any_default_set, 1),
ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
BT_DISCOVERY_STOPPED),
TEST_CASE_BREDRLE("Bluetooth Device Get UUIDS - Success",
ACTION_SUCCESS(bluetooth_enable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
- ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
ACTION_SUCCESS(bt_start_discovery_action, NULL),
CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
BT_DISCOVERY_STARTED),
+ ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
+ CALLBACK_DEVICE_FOUND(prop_emu_remote_any_default_set, 1),
ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
BT_DISCOVERY_STOPPED),
TEST_CASE_BREDRLE("Bluetooth Device Get COD - Success",
ACTION_SUCCESS(bluetooth_enable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
- ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
ACTION_SUCCESS(bt_start_discovery_action, NULL),
CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
BT_DISCOVERY_STARTED),
+ ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
+ CALLBACK_DEVICE_FOUND(prop_emu_remote_any_default_set, 1),
ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
BT_DISCOVERY_STOPPED),
TEST_CASE_BREDRLE("Bluetooth Device Get TOD - Success",
ACTION_SUCCESS(bluetooth_enable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
- ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
ACTION_SUCCESS(bt_start_discovery_action, NULL),
CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
BT_DISCOVERY_STARTED),
+ ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
+ CALLBACK_DEVICE_FOUND(prop_emu_remote_any_default_set, 1),
ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
BT_DISCOVERY_STOPPED),
TEST_CASE_BREDRLE("Bluetooth Device Get RSSI - Success",
ACTION_SUCCESS(bluetooth_enable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
- ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
ACTION_SUCCESS(bt_start_discovery_action, NULL),
CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
BT_DISCOVERY_STARTED),
+ ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
+ CALLBACK_DEVICE_FOUND(prop_emu_remote_any_default_set, 1),
ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
BT_DISCOVERY_STOPPED),
TEST_CASE_BREDRLE("Bluetooth Device Get TIMESTAMP - Success",
ACTION_SUCCESS(bluetooth_enable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
- ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
ACTION_SUCCESS(bt_start_discovery_action, NULL),
CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
BT_DISCOVERY_STARTED),
+ ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
+ CALLBACK_DEVICE_FOUND(prop_emu_remote_any_default_set, 1),
ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
BT_DISCOVERY_STOPPED),
TEST_CASE_BREDRLE("Bluetooth Device Get BDADDR - Fail",
ACTION_SUCCESS(bluetooth_enable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
- ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
ACTION_SUCCESS(bt_start_discovery_action, NULL),
CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
BT_DISCOVERY_STARTED),
+ ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
+ CALLBACK_DEVICE_FOUND(prop_emu_remote_any_default_set, 1),
ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
BT_DISCOVERY_STOPPED),
TEST_CASE_BREDRLE("Bluetooth Device Get SCAN_MODE - Fail",
ACTION_SUCCESS(bluetooth_enable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
- ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
ACTION_SUCCESS(bt_start_discovery_action, NULL),
CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
BT_DISCOVERY_STARTED),
+ ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
+ CALLBACK_DEVICE_FOUND(prop_emu_remote_any_default_set, 1),
ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
BT_DISCOVERY_STOPPED),
TEST_CASE_BREDRLE("Bluetooth Device Get BONDED_DEVICES - Fail",
ACTION_SUCCESS(bluetooth_enable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
- ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
ACTION_SUCCESS(bt_start_discovery_action, NULL),
CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
BT_DISCOVERY_STARTED),
+ ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
+ CALLBACK_DEVICE_FOUND(prop_emu_remote_any_default_set, 1),
ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
BT_DISCOVERY_STOPPED),
TEST_CASE_BREDRLE("Bluetooth Device Get DISCOVERY_TIMEOUT - Fail",
ACTION_SUCCESS(bluetooth_enable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
- ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
ACTION_SUCCESS(bt_start_discovery_action, NULL),
CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
BT_DISCOVERY_STARTED),
+ ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
+ CALLBACK_DEVICE_FOUND(prop_emu_remote_any_default_set, 1),
ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
BT_DISCOVERY_STOPPED),
TEST_CASE_BREDRLE("Bluetooth Device Get VERSION_INFO - Fail",
ACTION_SUCCESS(bluetooth_enable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
- ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
ACTION_SUCCESS(bt_start_discovery_action, NULL),
CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
BT_DISCOVERY_STARTED),
+ ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
+ CALLBACK_DEVICE_FOUND(prop_emu_remote_any_default_set, 1),
ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
BT_DISCOVERY_STOPPED),
TEST_CASE_BREDRLE("Bluetooth Device Get FRIENDLY_NAME - Fail",
ACTION_SUCCESS(bluetooth_enable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
- ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
ACTION_SUCCESS(bt_start_discovery_action, NULL),
CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
BT_DISCOVERY_STARTED),
+ ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
+ CALLBACK_DEVICE_FOUND(prop_emu_remote_any_default_set, 1),
ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
BT_DISCOVERY_STOPPED),
TEST_CASE_BREDRLE("Bluetooth Device Set FRIENDLY_NAME - Success",
ACTION_SUCCESS(bluetooth_enable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
- ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
ACTION_SUCCESS(bt_start_discovery_action, NULL),
CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
BT_DISCOVERY_STARTED),
+ ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
+ CALLBACK_DEVICE_FOUND(prop_emu_remote_any_default_set, 1),
ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
BT_DISCOVERY_STOPPED),
TEST_CASE_BREDRLE("Bluetooth Device Set BDNAME - Fail",
ACTION_SUCCESS(bluetooth_enable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
- ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
ACTION_SUCCESS(bt_start_discovery_action, NULL),
CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
BT_DISCOVERY_STARTED),
+ ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
+ CALLBACK_DEVICE_FOUND(prop_emu_remote_any_default_set, 1),
ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
BT_DISCOVERY_STOPPED),
TEST_CASE_BREDRLE("Bluetooth Device Set UUIDS - Fail",
ACTION_SUCCESS(bluetooth_enable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
- ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
ACTION_SUCCESS(bt_start_discovery_action, NULL),
CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
BT_DISCOVERY_STARTED),
+ ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
+ CALLBACK_DEVICE_FOUND(prop_emu_remote_any_default_set, 1),
ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
BT_DISCOVERY_STOPPED),
TEST_CASE_BREDRLE("Bluetooth Device Set COD - Fail",
ACTION_SUCCESS(bluetooth_enable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
- ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
ACTION_SUCCESS(bt_start_discovery_action, NULL),
CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
BT_DISCOVERY_STARTED),
+ ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
+ CALLBACK_DEVICE_FOUND(prop_emu_remote_any_default_set, 1),
ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
BT_DISCOVERY_STOPPED),
TEST_CASE_BREDRLE("Bluetooth Device Set TOD - Fail",
ACTION_SUCCESS(bluetooth_enable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
- ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
ACTION_SUCCESS(bt_start_discovery_action, NULL),
CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
BT_DISCOVERY_STARTED),
+ ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
+ CALLBACK_DEVICE_FOUND(prop_emu_remote_any_default_set, 1),
ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
BT_DISCOVERY_STOPPED),
TEST_CASE_BREDRLE("Bluetooth Device Set RSSI - Fail",
ACTION_SUCCESS(bluetooth_enable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
- ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
ACTION_SUCCESS(bt_start_discovery_action, NULL),
CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
BT_DISCOVERY_STARTED),
+ ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
+ CALLBACK_DEVICE_FOUND(prop_emu_remote_any_default_set, 1),
ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
BT_DISCOVERY_STOPPED),
TEST_CASE_BREDRLE("Bluetooth Device Set TIMESTAMP - Fail",
ACTION_SUCCESS(bluetooth_enable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
- ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
ACTION_SUCCESS(bt_start_discovery_action, NULL),
CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
BT_DISCOVERY_STARTED),
+ ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
+ CALLBACK_DEVICE_FOUND(prop_emu_remote_any_default_set, 1),
ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
BT_DISCOVERY_STOPPED),
TEST_CASE_BREDRLE("Bluetooth Device Set BDADDR - Fail",
ACTION_SUCCESS(bluetooth_enable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
- ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
ACTION_SUCCESS(bt_start_discovery_action, NULL),
CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
BT_DISCOVERY_STARTED),
+ ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
+ CALLBACK_DEVICE_FOUND(prop_emu_remote_any_default_set, 1),
ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
BT_DISCOVERY_STOPPED),
TEST_CASE_BREDRLE("Bluetooth Device Set SERVICE_RECORD - Fail",
ACTION_SUCCESS(bluetooth_enable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
- ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
ACTION_SUCCESS(bt_start_discovery_action, NULL),
CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
BT_DISCOVERY_STARTED),
+ ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
+ CALLBACK_DEVICE_FOUND(prop_emu_remote_any_default_set, 1),
ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
BT_DISCOVERY_STOPPED),
TEST_CASE_BREDRLE("Bluetooth Device Set SCAN_MODE - Fail",
ACTION_SUCCESS(bluetooth_enable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
- ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
ACTION_SUCCESS(bt_start_discovery_action, NULL),
CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
BT_DISCOVERY_STARTED),
+ ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
+ CALLBACK_DEVICE_FOUND(prop_emu_remote_any_default_set, 1),
ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
BT_DISCOVERY_STOPPED),
TEST_CASE_BREDRLE("Bluetooth Device Set BONDED_DEVICES - Fail",
ACTION_SUCCESS(bluetooth_enable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
- ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
ACTION_SUCCESS(bt_start_discovery_action, NULL),
CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
BT_DISCOVERY_STARTED),
+ ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
+ CALLBACK_DEVICE_FOUND(prop_emu_remote_any_default_set, 1),
ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
BT_DISCOVERY_STOPPED),
TEST_CASE_BREDRLE("Bluetooth Device Set DISCOVERY_TIMEOUT - Fail",
ACTION_SUCCESS(bluetooth_enable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
- ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
ACTION_SUCCESS(bt_start_discovery_action, NULL),
CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
BT_DISCOVERY_STARTED),
+ ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
+ CALLBACK_DEVICE_FOUND(prop_emu_remote_any_default_set, 1),
ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
BT_DISCOVERY_STOPPED),
TEST_CASE_BREDR("Bluetooth Create Bond PIN - Success",
ACTION_SUCCESS(bluetooth_enable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
- ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
ACTION_SUCCESS(emu_set_pin_code_action, &emu_pin_set_req),
ACTION_SUCCESS(bt_start_discovery_action, NULL),
CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
BT_DISCOVERY_STARTED),
- CALLBACK_DEVICE_FOUND(prop_emu_remotes_default_set, 3),
+ ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
+ CALLBACK_DEVICE_FOUND(prop_emu_remote_bredr_default_set, 3),
ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
BT_DISCOVERY_STOPPED),
TEST_CASE_BREDR("Bluetooth Create Bond PIN - Bad PIN",
ACTION_SUCCESS(bluetooth_enable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
- ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
ACTION_SUCCESS(emu_set_pin_code_action, &emu_pin_set_req),
ACTION_SUCCESS(bt_start_discovery_action, NULL),
CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
BT_DISCOVERY_STARTED),
- CALLBACK_DEVICE_FOUND(prop_emu_remotes_default_set, 3),
+ ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
+ CALLBACK_DEVICE_FOUND(prop_emu_remote_bredr_default_set, 3),
ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
BT_DISCOVERY_STOPPED),
TEST_CASE_BREDR("Bluetooth Create Bond SSP -Success",
ACTION_SUCCESS(bluetooth_enable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
- ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
ACTION_SUCCESS(emu_set_io_cap, &display_yes_no_io_cap),
ACTION_SUCCESS(bt_start_discovery_action, NULL),
CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
BT_DISCOVERY_STARTED),
- CALLBACK_DEVICE_FOUND(prop_emu_remotes_default_set, 3),
+ ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
+ CALLBACK_DEVICE_FOUND(prop_emu_remote_bredr_default_set, 3),
ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
BT_DISCOVERY_STOPPED),
TEST_CASE_BREDR("Bluetooth Create Bond SSP - Negative reply",
ACTION_SUCCESS(bluetooth_enable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
- ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
ACTION_SUCCESS(emu_set_io_cap, &display_yes_no_io_cap),
ACTION_SUCCESS(bt_start_discovery_action, NULL),
CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
BT_DISCOVERY_STARTED),
- CALLBACK_DEVICE_FOUND(prop_emu_remotes_default_set, 3),
+ ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
+ CALLBACK_DEVICE_FOUND(prop_emu_remote_bredr_default_set, 3),
ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
BT_DISCOVERY_STOPPED),
TEST_CASE_BREDR("Bluetooth Cancel Bonding - Success",
ACTION_SUCCESS(bluetooth_enable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
- ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
ACTION_SUCCESS(emu_set_io_cap, &display_yes_no_io_cap),
ACTION_SUCCESS(bt_start_discovery_action, NULL),
CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
BT_DISCOVERY_STARTED),
- CALLBACK_DEVICE_FOUND(prop_emu_remotes_default_set, 3),
+ ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
+ CALLBACK_DEVICE_FOUND(prop_emu_remote_bredr_default_set, 3),
ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
BT_DISCOVERY_STOPPED),
TEST_CASE_BREDR("Bluetooth Remove Bond - Success",
ACTION_SUCCESS(bluetooth_enable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
- ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
ACTION_SUCCESS(emu_set_io_cap, &display_yes_no_io_cap),
ACTION_SUCCESS(bt_start_discovery_action, NULL),
CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
BT_DISCOVERY_STARTED),
- CALLBACK_DEVICE_FOUND(prop_emu_remotes_default_set, 3),
+ ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
+ CALLBACK_DEVICE_FOUND(prop_emu_remote_bredr_default_set, 3),
ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
BT_DISCOVERY_STOPPED),
schedule_action_verification(step);
}
+static void trigger_device_found(void *user_data)
+{
+ emu_setup_powered_remote_action();
+}
+
+static void delayemu_setup_powered_remote_action(void)
+{
+ /* Make sure discovery is enabled before enabling advertising.
+ * Unfortunately GATT HAL doesn't have discovering callback like
+ * Bluetooth HAL so we need to delay
+ */
+ tester_wait(1, trigger_device_found, NULL);
+}
+
static struct test_case test_cases[] = {
TEST_CASE_BREDRLE("Gatt Init",
ACTION_SUCCESS(dummy_action, NULL),
TEST_CASE_BREDRLE("Gatt Client - Scan",
ACTION_SUCCESS(bluetooth_enable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
- ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
ACTION_SUCCESS(gatt_client_register_action, &app1_uuid),
CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS),
ACTION_SUCCESS(gatt_client_start_scan_action, NULL),
+ ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL),
CALLBACK_DEVICE_FOUND(prop_emu_remotes_default_le_set, 2),
CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE),
ACTION_SUCCESS(gatt_client_stop_scan_action, NULL),
TEST_CASE_BREDRLE("Gatt Client - LE Connect",
ACTION_SUCCESS(bluetooth_enable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
- ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
ACTION_SUCCESS(gatt_client_register_action, &app1_uuid),
CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS),
ACTION_SUCCESS(gatt_client_start_scan_action, NULL),
+ ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL),
CALLBACK_DEVICE_FOUND(prop_emu_remotes_default_le_set, 2),
CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE),
ACTION_SUCCESS(gatt_client_stop_scan_action, NULL),
TEST_CASE_BREDRLE("Gatt Client - LE Disconnect",
ACTION_SUCCESS(bluetooth_enable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
- ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
ACTION_SUCCESS(gatt_client_register_action, &app1_uuid),
CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS),
ACTION_SUCCESS(gatt_client_start_scan_action, NULL),
+ ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL),
CALLBACK_DEVICE_FOUND(prop_emu_remotes_default_le_set, 2),
CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE),
ACTION_SUCCESS(gatt_client_stop_scan_action, NULL),
TEST_CASE_BREDRLE("Gatt Client - LE Multiple Client Conn./Disc.",
ACTION_SUCCESS(bluetooth_enable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
- ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
ACTION_SUCCESS(gatt_client_register_action, &app1_uuid),
ACTION_SUCCESS(gatt_client_register_action, &app2_uuid),
CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS),
ACTION_SUCCESS(gatt_client_start_scan_action, NULL),
+ ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL),
CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE),
ACTION_SUCCESS(gatt_client_stop_scan_action, NULL),
ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req),
TEST_CASE_BREDRLE("Gatt Client - Listen and Disconnect",
ACTION_SUCCESS(bluetooth_enable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
- ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
+ ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL),
ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
ACTION_SUCCESS(bt_set_property_action,
TEST_CASE_BREDRLE("Gatt Client - Double Listen",
ACTION_SUCCESS(bluetooth_enable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
- ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
+ ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL),
ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
ACTION_SUCCESS(bt_set_property_action,
ACTION_SUCCESS(init_pdus, search_service),
ACTION_SUCCESS(bluetooth_enable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
- ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
ACTION_SUCCESS(gatt_client_register_action, &app1_uuid),
CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS),
ACTION_SUCCESS(gatt_client_start_scan_action, NULL),
+ ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL),
CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE),
ACTION_SUCCESS(gatt_client_stop_scan_action, NULL),
ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req),
ACTION_SUCCESS(init_pdus, search_service_2),
ACTION_SUCCESS(bluetooth_enable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
- ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
ACTION_SUCCESS(gatt_client_register_action, &app1_uuid),
CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS),
ACTION_SUCCESS(gatt_client_start_scan_action, NULL),
+ ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL),
CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE),
ACTION_SUCCESS(gatt_client_stop_scan_action, NULL),
ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req),
ACTION_SUCCESS(init_pdus, search_service_3),
ACTION_SUCCESS(bluetooth_enable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
- ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
ACTION_SUCCESS(gatt_client_register_action, &app1_uuid),
CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS),
ACTION_SUCCESS(gatt_client_start_scan_action, NULL),
+ ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL),
CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE),
ACTION_SUCCESS(gatt_client_stop_scan_action, NULL),
ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req),
ACTION_SUCCESS(init_pdus, search_service_4),
ACTION_SUCCESS(bluetooth_enable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
- ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
ACTION_SUCCESS(gatt_client_register_action, &app1_uuid),
CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS),
ACTION_SUCCESS(gatt_client_start_scan_action, NULL),
+ ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL),
CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE),
ACTION_SUCCESS(gatt_client_stop_scan_action, NULL),
ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req),
ACTION_SUCCESS(init_pdus, get_characteristic_1),
ACTION_SUCCESS(bluetooth_enable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
- ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
ACTION_SUCCESS(gatt_client_register_action, &app1_uuid),
CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS),
ACTION_SUCCESS(gatt_client_start_scan_action, NULL),
+ ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL),
CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE),
ACTION_SUCCESS(gatt_client_stop_scan_action, NULL),
ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req),
ACTION_SUCCESS(init_pdus, get_characteristic_2),
ACTION_SUCCESS(bluetooth_enable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
- ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
ACTION_SUCCESS(gatt_client_register_action, &app1_uuid),
CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS),
ACTION_SUCCESS(gatt_client_start_scan_action, NULL),
+ ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL),
CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE),
ACTION_SUCCESS(gatt_client_stop_scan_action, NULL),
ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req),
ACTION_SUCCESS(init_pdus, get_characteristic_1),
ACTION_SUCCESS(bluetooth_enable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
- ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
ACTION_SUCCESS(gatt_client_register_action, &app1_uuid),
CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS),
ACTION_SUCCESS(gatt_client_start_scan_action, NULL),
+ ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL),
CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE),
ACTION_SUCCESS(gatt_client_stop_scan_action, NULL),
ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req),
ACTION_SUCCESS(init_pdus, get_descriptor_0),
ACTION_SUCCESS(bluetooth_enable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
- ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
ACTION_SUCCESS(gatt_client_register_action, &app1_uuid),
CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS),
ACTION_SUCCESS(gatt_client_start_scan_action, NULL),
+ ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL),
CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE),
ACTION_SUCCESS(gatt_client_stop_scan_action, NULL),
ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req),
ACTION_SUCCESS(init_pdus, get_descriptor_1),
ACTION_SUCCESS(bluetooth_enable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
- ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
ACTION_SUCCESS(gatt_client_register_action, &app1_uuid),
CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS),
ACTION_SUCCESS(gatt_client_start_scan_action, NULL),
+ ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL),
CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE),
ACTION_SUCCESS(gatt_client_stop_scan_action, NULL),
ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req),
ACTION_SUCCESS(init_pdus, get_descriptor_2),
ACTION_SUCCESS(bluetooth_enable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
- ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
ACTION_SUCCESS(gatt_client_register_action, &app1_uuid),
CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS),
ACTION_SUCCESS(gatt_client_start_scan_action, NULL),
+ ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL),
CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE),
ACTION_SUCCESS(gatt_client_stop_scan_action, NULL),
ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req),
ACTION_SUCCESS(init_pdus, get_descriptor_3),
ACTION_SUCCESS(bluetooth_enable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
- ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
ACTION_SUCCESS(gatt_client_register_action, &app1_uuid),
CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS),
ACTION_SUCCESS(gatt_client_start_scan_action, NULL),
+ ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL),
CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE),
ACTION_SUCCESS(gatt_client_stop_scan_action, NULL),
ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req),
ACTION_SUCCESS(init_pdus, get_included_0),
ACTION_SUCCESS(bluetooth_enable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
- ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
ACTION_SUCCESS(gatt_client_register_action, &app1_uuid),
CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS),
ACTION_SUCCESS(gatt_client_start_scan_action, NULL),
+ ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL),
CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE),
ACTION_SUCCESS(gatt_client_stop_scan_action, NULL),
ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req),
ACTION_SUCCESS(init_pdus, get_included_1),
ACTION_SUCCESS(bluetooth_enable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
- ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
ACTION_SUCCESS(gatt_client_register_action, &app1_uuid),
CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS),
ACTION_SUCCESS(gatt_client_start_scan_action, NULL),
+ ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL),
CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE),
ACTION_SUCCESS(gatt_client_stop_scan_action, NULL),
ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req),
ACTION_SUCCESS(init_pdus, get_included_2),
ACTION_SUCCESS(bluetooth_enable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
- ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
ACTION_SUCCESS(gatt_client_register_action, &app1_uuid),
CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS),
ACTION_SUCCESS(gatt_client_start_scan_action, NULL),
+ ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL),
CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE),
ACTION_SUCCESS(gatt_client_stop_scan_action, NULL),
ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req),
ACTION_SUCCESS(init_pdus, get_included_3),
ACTION_SUCCESS(bluetooth_enable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
- ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
ACTION_SUCCESS(gatt_client_register_action, &app1_uuid),
CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS),
ACTION_SUCCESS(gatt_client_start_scan_action, NULL),
+ ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL),
CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE),
ACTION_SUCCESS(gatt_client_stop_scan_action, NULL),
ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req),
ACTION_SUCCESS(bluetooth_enable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
ACTION_SUCCESS(init_read_params_action, &set_read_param_1),
- ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
ACTION_SUCCESS(gatt_client_register_action, &app1_uuid),
CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS),
ACTION_SUCCESS(gatt_client_start_scan_action, NULL),
+ ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL),
CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE),
ACTION_SUCCESS(gatt_client_stop_scan_action, NULL),
ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req),
ACTION_SUCCESS(bluetooth_enable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
ACTION_SUCCESS(init_read_params_action, &set_read_param_2),
- ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
ACTION_SUCCESS(gatt_client_register_action, &app1_uuid),
CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS),
ACTION_SUCCESS(gatt_client_start_scan_action, NULL),
+ ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL),
CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE),
ACTION_SUCCESS(gatt_client_stop_scan_action, NULL),
ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req),
ACTION_SUCCESS(bluetooth_enable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
ACTION_SUCCESS(init_read_params_action, &set_read_param_3),
- ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
ACTION_SUCCESS(gatt_client_register_action, &app1_uuid),
CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS),
ACTION_SUCCESS(gatt_client_start_scan_action, NULL),
+ ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL),
CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE),
ACTION_SUCCESS(gatt_client_stop_scan_action, NULL),
ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req),
ACTION_SUCCESS(init_read_params_action, &set_read_param_4),
ACTION_SUCCESS(bluetooth_enable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
- ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
ACTION_SUCCESS(gatt_client_register_action, &app1_uuid),
CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS),
ACTION_SUCCESS(gatt_client_start_scan_action, NULL),
+ ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL),
CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE),
ACTION_SUCCESS(gatt_client_stop_scan_action, NULL),
ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req),
ACTION_SUCCESS(init_read_params_action, &set_read_param_5),
ACTION_SUCCESS(bluetooth_enable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
- ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
ACTION_SUCCESS(gatt_client_register_action, &app1_uuid),
CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS),
ACTION_SUCCESS(gatt_client_start_scan_action, NULL),
+ ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL),
CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE),
ACTION_SUCCESS(gatt_client_stop_scan_action, NULL),
ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req),
ACTION_SUCCESS(init_read_params_action, &set_read_param_6),
ACTION_SUCCESS(bluetooth_enable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
- ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
ACTION_SUCCESS(gatt_client_register_action, &app1_uuid),
CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS),
ACTION_SUCCESS(gatt_client_start_scan_action, NULL),
+ ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL),
CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE),
ACTION_SUCCESS(gatt_client_stop_scan_action, NULL),
ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req),
ACTION_SUCCESS(bluetooth_enable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
ACTION_SUCCESS(init_write_params_action, &set_write_param_1),
- ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
ACTION_SUCCESS(gatt_client_register_action, &app1_uuid),
CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS),
ACTION_SUCCESS(gatt_client_start_scan_action, NULL),
+ ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL),
CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE),
ACTION_SUCCESS(gatt_client_stop_scan_action, NULL),
ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req),
ACTION_SUCCESS(bluetooth_enable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
ACTION_SUCCESS(init_write_params_action, &set_write_param_1),
- ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
ACTION_SUCCESS(gatt_client_register_action, &app1_uuid),
CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS),
ACTION_SUCCESS(gatt_client_start_scan_action, NULL),
+ ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL),
CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE),
ACTION_SUCCESS(gatt_client_stop_scan_action, NULL),
ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req),
ACTION_SUCCESS(bluetooth_enable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
ACTION_SUCCESS(init_write_params_action, &set_write_param_2),
- ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
ACTION_SUCCESS(gatt_client_register_action, &app1_uuid),
CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS),
ACTION_SUCCESS(gatt_client_start_scan_action, NULL),
+ ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL),
CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE),
ACTION_SUCCESS(gatt_client_stop_scan_action, NULL),
ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req),
ACTION_SUCCESS(bluetooth_enable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
ACTION_SUCCESS(init_write_params_action, &set_write_param_3),
- ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
ACTION_SUCCESS(gatt_client_register_action, &app1_uuid),
CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS),
ACTION_SUCCESS(gatt_client_start_scan_action, NULL),
+ ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL),
CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE),
ACTION_SUCCESS(gatt_client_stop_scan_action, NULL),
ACTION_FAIL(gatt_client_write_characteristic_action,
ACTION_SUCCESS(init_pdus, notification_1),
ACTION_SUCCESS(bluetooth_enable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
- ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
ACTION_SUCCESS(gatt_client_register_action, &app1_uuid),
CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS),
ACTION_SUCCESS(gatt_client_start_scan_action, NULL),
+ ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL),
CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE),
ACTION_SUCCESS(gatt_client_stop_scan_action, NULL),
ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req),
ACTION_SUCCESS(init_pdus, notification_1),
ACTION_SUCCESS(bluetooth_enable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
- ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
ACTION_SUCCESS(gatt_client_register_action, &app1_uuid),
CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS),
ACTION_SUCCESS(gatt_client_start_scan_action, NULL),
+ ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL),
CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE),
ACTION_SUCCESS(gatt_client_stop_scan_action, NULL),
ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req),
ACTION_SUCCESS(init_notify_params_action, &set_notify_param_1),
ACTION_SUCCESS(bluetooth_enable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
- ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
ACTION_SUCCESS(gatt_client_register_action, &app1_uuid),
CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS),
ACTION_SUCCESS(gatt_client_start_scan_action, NULL),
+ ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL),
CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE),
ACTION_SUCCESS(gatt_client_stop_scan_action, NULL),
ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req),
ACTION_SUCCESS(init_notify_params_action, &set_notify_param_2),
ACTION_SUCCESS(bluetooth_enable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
- ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
ACTION_SUCCESS(gatt_client_register_action, &app1_uuid),
CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS),
ACTION_SUCCESS(gatt_client_start_scan_action, NULL),
+ ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL),
CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE),
ACTION_SUCCESS(gatt_client_stop_scan_action, NULL),
ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req),
ACTION_SUCCESS(bluetooth_enable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
ACTION_SUCCESS(init_write_params_action, &set_write_param_4),
- ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
ACTION_SUCCESS(gatt_client_register_action, &app1_uuid),
CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS),
ACTION_SUCCESS(gatt_client_start_scan_action, NULL),
+ ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL),
CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE),
ACTION_SUCCESS(gatt_client_stop_scan_action, NULL),
ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req),
ACTION_SUCCESS(bluetooth_enable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
ACTION_SUCCESS(init_write_params_action, &set_write_param_6),
- ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
ACTION_SUCCESS(gatt_client_register_action, &app1_uuid),
CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS),
ACTION_SUCCESS(gatt_client_start_scan_action, NULL),
+ ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL),
CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE),
ACTION_SUCCESS(gatt_client_stop_scan_action, NULL),
ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req),
ACTION_SUCCESS(bluetooth_enable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
ACTION_SUCCESS(init_write_params_action, &set_write_param_5),
- ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
ACTION_SUCCESS(gatt_client_register_action, &app1_uuid),
CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS),
ACTION_SUCCESS(gatt_client_start_scan_action, NULL),
+ ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL),
CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE),
ACTION_SUCCESS(gatt_client_stop_scan_action, NULL),
ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req),
TEST_CASE_BREDRLE("Gatt Server - LE Connect",
ACTION_SUCCESS(bluetooth_enable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
- ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
ACTION_SUCCESS(gatt_server_register_action, &app1_uuid),
ACTION_SUCCESS(bt_start_discovery_action, NULL),
CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
BT_DISCOVERY_STARTED),
+ ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
CALLBACK_DEVICE_FOUND(prop_emu_remotes_default_le_set, 2),
ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
ACTION_SUCCESS(gatt_server_connect_action, &app1_conn_req),
TEST_CASE_BREDRLE("Gatt Server - LE Disconnect",
ACTION_SUCCESS(bluetooth_enable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
- ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
ACTION_SUCCESS(gatt_server_register_action, &app1_uuid),
ACTION_SUCCESS(bt_start_discovery_action, NULL),
CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
BT_DISCOVERY_STARTED),
+ ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
CALLBACK_DEVICE_FOUND(prop_emu_remotes_default_le_set, 2),
ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
ACTION_SUCCESS(gatt_server_connect_action, &app1_conn_req),
TEST_CASE_BREDRLE("Gatt Server - LE Multiple Server Conn./Disc",
ACTION_SUCCESS(bluetooth_enable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
- ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
ACTION_SUCCESS(gatt_server_register_action, &app1_uuid),
ACTION_SUCCESS(bt_start_discovery_action, NULL),
CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
BT_DISCOVERY_STARTED),
+ ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
CALLBACK_DEVICE_FOUND(prop_emu_remotes_default_le_set, 2),
ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
ACTION_SUCCESS(gatt_server_connect_action, &app1_conn_req),
ACTION_SUCCESS(init_pdus, send_indication_1),
ACTION_SUCCESS(bluetooth_enable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
- ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
ACTION_SUCCESS(gatt_server_register_action, &app1_uuid),
ACTION_SUCCESS(bt_start_discovery_action, NULL),
CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
BT_DISCOVERY_STARTED),
+ ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
CALLBACK_DEVICE_FOUND(prop_emu_remotes_default_le_set, 2),
ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
ACTION_SUCCESS(gatt_server_connect_action, &app1_conn_req),
ACTION_SUCCESS(init_pdus, send_notification_1),
ACTION_SUCCESS(bluetooth_enable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
- ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
ACTION_SUCCESS(gatt_server_register_action, &app1_uuid),
ACTION_SUCCESS(bt_start_discovery_action, NULL),
CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
BT_DISCOVERY_STARTED),
+ ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
CALLBACK_DEVICE_FOUND(prop_emu_remotes_default_le_set, 2),
ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
ACTION_SUCCESS(gatt_server_connect_action, &app1_conn_req),
TEST_CASE_BREDRLE("Gatt Server - Send Notification, wrong conn id",
ACTION_SUCCESS(bluetooth_enable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
- ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
ACTION_SUCCESS(gatt_server_register_action, &app1_uuid),
ACTION_SUCCESS(bt_start_discovery_action, NULL),
CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
BT_DISCOVERY_STARTED),
+ ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
CALLBACK_DEVICE_FOUND(prop_emu_remotes_default_le_set, 2),
ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
ACTION_SUCCESS(gatt_server_connect_action, &app1_conn_req),
TEST_CASE_BREDRLE("Gatt Server - Send response to read char request",
ACTION_SUCCESS(bluetooth_enable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
- ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
ACTION_SUCCESS(gatt_server_register_action, &app1_uuid),
ACTION_SUCCESS(bt_start_discovery_action, NULL),
CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
BT_DISCOVERY_STARTED),
+ ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
CALLBACK_DEVICE_FOUND(prop_emu_remotes_default_le_set, 2),
ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
ACTION_SUCCESS(gatt_server_connect_action, &app1_conn_req),
TEST_CASE_BREDRLE("Gatt Server - Send response to write char request",
ACTION_SUCCESS(bluetooth_enable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
- ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
ACTION_SUCCESS(gatt_server_register_action, &app1_uuid),
ACTION_SUCCESS(bt_start_discovery_action, NULL),
CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
BT_DISCOVERY_STARTED),
+ ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
CALLBACK_DEVICE_FOUND(prop_emu_remotes_default_le_set, 2),
ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
ACTION_SUCCESS(gatt_server_connect_action, &app1_conn_req),
TEST_CASE_BREDRLE("Gatt Server - Find By Type - Attribute not found",
ACTION_SUCCESS(bluetooth_enable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
- ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
ACTION_SUCCESS(gatt_server_register_action, &app1_uuid),
ACTION_SUCCESS(bt_start_discovery_action, NULL),
CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
BT_DISCOVERY_STARTED),
+ ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
CALLBACK_DEVICE_FOUND(prop_emu_remotes_default_le_set, 2),
ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
ACTION_SUCCESS(gatt_server_connect_action, &app1_conn_req),
TEST_CASE_BREDRLE("Gatt Server - Srvc change write req. success",
ACTION_SUCCESS(bluetooth_enable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
- ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
ACTION_SUCCESS(gatt_server_register_action, &app1_uuid),
ACTION_SUCCESS(bt_start_discovery_action, NULL),
CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
BT_DISCOVERY_STARTED),
+ ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
CALLBACK_DEVICE_FOUND(prop_emu_remotes_default_le_set, 2),
ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
ACTION_SUCCESS(gatt_server_connect_action, &app1_conn_req),
TEST_CASE_BREDRLE("Gatt Server - Send error resp to write char request",
ACTION_SUCCESS(bluetooth_enable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
- ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
ACTION_SUCCESS(gatt_server_register_action, &app1_uuid),
ACTION_SUCCESS(bt_start_discovery_action, NULL),
CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
BT_DISCOVERY_STARTED),
+ ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
CALLBACK_DEVICE_FOUND(prop_emu_remotes_default_le_set, 2),
ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
ACTION_SUCCESS(gatt_server_connect_action, &app1_conn_req),
if ((data->hciemu_type == HCIEMU_TYPE_LE) ||
(data->hciemu_type == HCIEMU_TYPE_BREDRLE)) {
- uint8_t adv[4];
+ uint8_t adv[3];
adv[0] = 0x02; /* Field length */
adv[1] = 0x01; /* Flags */
adv[2] = 0x02; /* Flags value */
- adv[3] = 0x00; /* Field terminator */
bthost_set_adv_data(bthost, adv, sizeof(adv));
bthost_set_adv_enable(bthost, 0x01);
--mandir=/usr/share/man \
--sysconfdir=/etc \
--localstatedir=/var \
+ --enable-tools \
--enable-manpages \
--enable-backtrace \
+ --enable-testing \
--enable-experimental \
+ --enable-deprecated \
--enable-nfc \
--enable-sap \
--enable-health \
--enable-android \
--enable-sixaxis \
+ --enable-midi \
+ --enable-mesh \
--disable-datafiles $*
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
-#include <readline/readline.h>
-#include <wordexp.h>
+#include <stdbool.h>
+#include <string.h>
#include "gdbus/gdbus.h"
-#include "display.h"
+#include "src/shared/shell.h"
#include "advertising.h"
#define AD_PATH "/org/bluez/advertising"
#define AD_IFACE "org.bluez.LEAdvertisement1"
-static gboolean registered = FALSE;
-static char *ad_type = NULL;
-static char **ad_uuids = NULL;
-static size_t ad_uuids_len = 0;
-static char *ad_service_uuid = NULL;
-static uint8_t ad_service_data[25];
-static uint8_t ad_service_data_len = 0;
-static uint16_t ad_manufacturer_id;
-static uint8_t ad_manufacturer_data[25];
-static uint8_t ad_manufacturer_data_len = 0;
-static gboolean ad_tx_power = FALSE;
+struct ad_data {
+ uint8_t data[25];
+ uint8_t len;
+};
+
+struct service_data {
+ char *uuid;
+ struct ad_data data;
+};
+
+struct manufacturer_data {
+ uint16_t id;
+ struct ad_data data;
+};
+
+static struct ad {
+ bool registered;
+ char *type;
+ char *local_name;
+ uint16_t local_appearance;
+ uint16_t duration;
+ uint16_t timeout;
+ char **uuids;
+ size_t uuids_len;
+ struct service_data service;
+ struct manufacturer_data manufacturer;
+ bool tx_power;
+ bool name;
+ bool appearance;
+} ad = {
+ .local_appearance = UINT16_MAX,
+};
static void ad_release(DBusConnection *conn)
{
- registered = FALSE;
+ ad.registered = false;
g_dbus_unregister_interface(conn, AD_PATH, AD_IFACE);
}
static DBusMessage *release_advertising(DBusConnection *conn,
DBusMessage *msg, void *user_data)
{
- rl_printf("Advertising released\n");
+ bt_shell_printf("Advertising released\n");
ad_release(conn);
dbus_error_init(&error);
if (dbus_set_error_from_message(&error, message) == FALSE) {
- registered = TRUE;
- rl_printf("Advertising object registered\n");
+ ad.registered = true;
+ bt_shell_printf("Advertising object registered\n");
} else {
- rl_printf("Failed to register advertisement: %s\n", error.name);
+ bt_shell_printf("Failed to register advertisement: %s\n", error.name);
dbus_error_free(&error);
if (g_dbus_unregister_interface(conn, AD_PATH,
AD_IFACE) == FALSE)
- rl_printf("Failed to unregister advertising object\n");
+ bt_shell_printf("Failed to unregister advertising object\n");
}
}
{
const char *type = "peripheral";
- if (!ad_type || strlen(ad_type) > 0)
- type = ad_type;
+ if (ad.type && strlen(ad.type) > 0)
+ type = ad.type;
dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &type);
static gboolean uuids_exists(const GDBusPropertyTable *property, void *data)
{
- return ad_uuids_len != 0;
+ return ad.uuids_len != 0;
}
static gboolean get_uuids(const GDBusPropertyTable *property,
dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "as", &array);
- for (i = 0; i < ad_uuids_len; i++)
+ for (i = 0; i < ad.uuids_len; i++)
dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING,
- &ad_uuids[i]);
+ &ad.uuids[i]);
dbus_message_iter_close_container(iter, &array);
static gboolean service_data_exists(const GDBusPropertyTable *property,
void *data)
{
- return ad_service_uuid != NULL;
+ return ad.service.uuid != NULL;
}
static gboolean get_service_data(const GDBusPropertyTable *property,
DBusMessageIter *iter, void *user_data)
{
DBusMessageIter dict;
- const uint8_t *data = ad_service_data;
+ struct ad_data *data = &ad.service.data;
+ uint8_t *val = data->data;
dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "{sv}", &dict);
- dict_append_array(&dict, ad_service_uuid, DBUS_TYPE_BYTE, &data,
- ad_service_data_len);
+ dict_append_array(&dict, ad.service.uuid, DBUS_TYPE_BYTE, &val,
+ data->len);
dbus_message_iter_close_container(iter, &dict);
static gboolean manufacturer_data_exists(const GDBusPropertyTable *property,
void *data)
{
- return ad_manufacturer_id != 0;
+ return ad.manufacturer.id != 0;
}
static gboolean get_manufacturer_data(const GDBusPropertyTable *property,
DBusMessageIter *iter, void *user_data)
{
DBusMessageIter dict;
- const uint8_t *data = ad_manufacturer_data;
+ struct ad_data *data = &ad.manufacturer.data;
+ uint8_t *val = data->data;
dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "{qv}", &dict);
- dict_append_basic_array(&dict, DBUS_TYPE_UINT16, &ad_manufacturer_id,
- DBUS_TYPE_BYTE, &data,
- ad_manufacturer_data_len);
+ dict_append_basic_array(&dict, DBUS_TYPE_UINT16, &ad.manufacturer.id,
+ DBUS_TYPE_BYTE, &val, data->len);
dbus_message_iter_close_container(iter, &dict);
return TRUE;
}
-static gboolean tx_power_exists(const GDBusPropertyTable *property, void *data)
+static gboolean includes_exists(const GDBusPropertyTable *property, void *data)
+{
+ return ad.tx_power || ad.name || ad.appearance;
+}
+
+static gboolean get_includes(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *user_data)
+{
+ DBusMessageIter array;
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "as", &array);
+
+ if (ad.tx_power) {
+ const char *str = "tx-power";
+
+ dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING, &str);
+ }
+
+ if (ad.name) {
+ const char *str = "local-name";
+
+ dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING, &str);
+ }
+
+ if (ad.appearance) {
+ const char *str = "appearance";
+
+ dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING, &str);
+ }
+
+ dbus_message_iter_close_container(iter, &array);
+
+
+ return TRUE;
+}
+
+static gboolean local_name_exits(const GDBusPropertyTable *property, void *data)
+{
+ return ad.local_name ? TRUE : FALSE;
+}
+
+static gboolean get_local_name(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *user_data)
+{
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &ad.local_name);
+
+ return TRUE;
+}
+
+static gboolean appearance_exits(const GDBusPropertyTable *property, void *data)
+{
+ return ad.local_appearance != UINT16_MAX ? TRUE : FALSE;
+}
+
+static gboolean get_appearance(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *user_data)
+{
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16,
+ &ad.local_appearance);
+
+ return TRUE;
+}
+
+static gboolean duration_exits(const GDBusPropertyTable *property, void *data)
+{
+ return ad.duration;
+}
+
+static gboolean get_duration(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *user_data)
+{
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &ad.duration);
+
+ return TRUE;
+}
+
+static gboolean timeout_exits(const GDBusPropertyTable *property, void *data)
{
- return ad_tx_power;
+ return ad.timeout;
}
-static gboolean get_tx_power(const GDBusPropertyTable *property,
+static gboolean get_timeout(const GDBusPropertyTable *property,
DBusMessageIter *iter, void *user_data)
{
- dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &ad_tx_power);
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &ad.timeout);
return TRUE;
}
{ "ServiceData", "a{sv}", get_service_data, NULL, service_data_exists },
{ "ManufacturerData", "a{qv}", get_manufacturer_data, NULL,
manufacturer_data_exists },
- { "IncludeTxPower", "b", get_tx_power, NULL, tx_power_exists },
+ { "Includes", "as", get_includes, NULL, includes_exists },
+ { "LocalName", "s", get_local_name, NULL, local_name_exits },
+ { "Appearance", "q", get_appearance, NULL, appearance_exits },
+ { "Duration", "q", get_duration, NULL, duration_exits },
+ { "Timeout", "q", get_timeout, NULL, timeout_exits },
{ }
};
void ad_register(DBusConnection *conn, GDBusProxy *manager, const char *type)
{
- if (registered == TRUE) {
- rl_printf("Advertisement is already registered\n");
+ if (ad.registered) {
+ bt_shell_printf("Advertisement is already registered\n");
return;
}
- ad_type = g_strdup(type);
+ g_free(ad.type);
+ ad.type = g_strdup(type);
if (g_dbus_register_interface(conn, AD_PATH, AD_IFACE, ad_methods,
NULL, ad_props, NULL, NULL) == FALSE) {
- rl_printf("Failed to register advertising object\n");
+ bt_shell_printf("Failed to register advertising object\n");
return;
}
if (g_dbus_proxy_method_call(manager, "RegisterAdvertisement",
register_setup, register_reply,
conn, NULL) == FALSE) {
- rl_printf("Failed to register advertising\n");
+ bt_shell_printf("Failed to register advertising\n");
return;
}
}
dbus_error_init(&error);
if (dbus_set_error_from_message(&error, message) == FALSE) {
- registered = FALSE;
- rl_printf("Advertising object unregistered\n");
+ ad.registered = false;
+ bt_shell_printf("Advertising object unregistered\n");
if (g_dbus_unregister_interface(conn, AD_PATH,
AD_IFACE) == FALSE)
- rl_printf("Failed to unregister advertising object\n");
+ bt_shell_printf("Failed to unregister advertising object\n");
} else {
- rl_printf("Failed to unregister advertisement: %s\n",
+ bt_shell_printf("Failed to unregister advertisement: %s\n",
error.name);
dbus_error_free(&error);
}
if (!manager)
ad_release(conn);
+ if (!ad.registered)
+ return;
+
+ g_free(ad.type);
+ ad.type = NULL;
+
if (g_dbus_proxy_method_call(manager, "UnregisterAdvertisement",
unregister_setup, unregister_reply,
conn, NULL) == FALSE) {
- rl_printf("Failed to unregister advertisement method\n");
+ bt_shell_printf("Failed to unregister advertisement method\n");
return;
}
}
-void ad_advertise_uuids(const char *arg)
+void ad_advertise_uuids(DBusConnection *conn, int argc, char *argv[])
{
- g_strfreev(ad_uuids);
- ad_uuids = NULL;
- ad_uuids_len = 0;
+ g_strfreev(ad.uuids);
+ ad.uuids = NULL;
+ ad.uuids_len = 0;
- if (!arg || !strlen(arg))
+ if (argc < 2 || !strlen(argv[1]))
return;
- ad_uuids = g_strsplit(arg, " ", -1);
- if (!ad_uuids) {
- rl_printf("Failed to parse input\n");
+ ad.uuids = g_strdupv(&argv[1]);
+ if (!ad.uuids) {
+ bt_shell_printf("Failed to parse input\n");
return;
}
- ad_uuids_len = g_strv_length(ad_uuids);
+ ad.uuids_len = g_strv_length(ad.uuids);
+
+ g_dbus_emit_property_changed(conn, AD_PATH, AD_IFACE, "ServiceUUIDs");
}
static void ad_clear_service(void)
{
- g_free(ad_service_uuid);
- ad_service_uuid = NULL;
- memset(ad_service_data, 0, sizeof(ad_service_data));
- ad_service_data_len = 0;
+ g_free(ad.service.uuid);
+ memset(&ad.service, 0, sizeof(ad.service));
}
-void ad_advertise_service(const char *arg)
+void ad_advertise_service(DBusConnection *conn, int argc, char *argv[])
{
- wordexp_t w;
unsigned int i;
-
- if (wordexp(arg, &w, WRDE_NOCMD)) {
- rl_printf("Invalid argument\n");
- return;
- }
+ struct ad_data *data;
ad_clear_service();
- if (w.we_wordc == 0)
- goto done;
+ if (argc < 2)
+ return;
- ad_service_uuid = g_strdup(w.we_wordv[0]);
+ ad.service.uuid = g_strdup(argv[1]);
+ data = &ad.service.data;
- for (i = 1; i < w.we_wordc; i++) {
+ for (i = 1; i < (unsigned int) argc; i++) {
long int val;
char *endptr = NULL;
- if (i >= G_N_ELEMENTS(ad_service_data)) {
- rl_printf("Too much data\n");
- goto done;
+ if (i >= G_N_ELEMENTS(data->data)) {
+ bt_shell_printf("Too much data\n");
+ ad_clear_service();
+ return;
}
- val = strtol(w.we_wordv[i], &endptr, 0);
+ val = strtol(argv[i], &endptr, 0);
if (!endptr || *endptr != '\0' || val > UINT8_MAX) {
- rl_printf("Invalid value at index %d\n", i);
+ bt_shell_printf("Invalid value at index %d\n", i);
ad_clear_service();
- goto done;
+ return;
}
- ad_service_data[ad_service_data_len] = val;
- ad_service_data_len++;
+ data->data[data->len] = val;
+ data->len++;
}
-done:
- wordfree(&w);
+ g_dbus_emit_property_changed(conn, AD_PATH, AD_IFACE, "ServiceData");
}
static void ad_clear_manufacturer(void)
{
- ad_manufacturer_id = 0;
- memset(ad_manufacturer_data, 0, sizeof(ad_manufacturer_data));
- ad_manufacturer_data_len = 0;
+ memset(&ad.manufacturer, 0, sizeof(ad.manufacturer));
}
-void ad_advertise_manufacturer(const char *arg)
+void ad_advertise_manufacturer(DBusConnection *conn, int argc, char *argv[])
{
- wordexp_t w;
unsigned int i;
char *endptr = NULL;
long int val;
-
- if (wordexp(arg, &w, WRDE_NOCMD)) {
- rl_printf("Invalid argument\n");
- return;
- }
+ struct ad_data *data;
ad_clear_manufacturer();
- if (w.we_wordc == 0)
- goto done;
+ if (argc < 2)
+ return;
- val = strtol(w.we_wordv[0], &endptr, 0);
+ val = strtol(argv[1], &endptr, 0);
if (!endptr || *endptr != '\0' || val > UINT16_MAX) {
- rl_printf("Invalid manufacture id\n");
- goto done;
+ bt_shell_printf("Invalid manufacture id\n");
+ return;
}
- ad_manufacturer_id = val;
+ ad.manufacturer.id = val;
+ data = &ad.manufacturer.data;
- for (i = 1; i < w.we_wordc; i++) {
- if (i >= G_N_ELEMENTS(ad_service_data)) {
- rl_printf("Too much data\n");
- goto done;
+ for (i = 2; i < (unsigned int) argc; i++) {
+ if (i >= G_N_ELEMENTS(data->data)) {
+ bt_shell_printf("Too much data\n");
+ ad_clear_manufacturer();
+ return;
}
- val = strtol(w.we_wordv[i], &endptr, 0);
+ val = strtol(argv[i], &endptr, 0);
if (!endptr || *endptr != '\0' || val > UINT8_MAX) {
- rl_printf("Invalid value at index %d\n", i);
- ad_clear_service();
- goto done;
+ bt_shell_printf("Invalid value at index %d\n", i);
+ ad_clear_manufacturer();
+ return;
}
- ad_manufacturer_data[ad_manufacturer_data_len] = val;
- ad_manufacturer_data_len++;
+ data->data[data->len] = val;
+ data->len++;
}
-done:
- wordfree(&w);
+ g_dbus_emit_property_changed(conn, AD_PATH, AD_IFACE,
+ "ManufacturerData");
+}
+
+void ad_advertise_tx_power(DBusConnection *conn, bool value)
+{
+ if (ad.tx_power == value)
+ return;
+
+ ad.tx_power = value;
+
+ g_dbus_emit_property_changed(conn, AD_PATH, AD_IFACE, "Includes");
}
+void ad_advertise_name(DBusConnection *conn, bool value)
+{
+ if (ad.name == value)
+ return;
+
+ ad.name = value;
+
+ if (!value) {
+ g_free(ad.local_name);
+ ad.local_name = NULL;
+ }
+
+ g_dbus_emit_property_changed(conn, AD_PATH, AD_IFACE, "Includes");
+}
-void ad_advertise_tx_power(gboolean value)
+void ad_advertise_local_name(DBusConnection *conn, const char *name)
{
- ad_tx_power = value;
+ if (ad.local_name && !strcmp(name, ad.local_name))
+ return;
+
+ g_free(ad.local_name);
+ ad.local_name = strdup(name);
+
+ g_dbus_emit_property_changed(conn, AD_PATH, AD_IFACE, "LocalName");
+}
+
+void ad_advertise_appearance(DBusConnection *conn, bool value)
+{
+ if (ad.appearance == value)
+ return;
+
+ ad.appearance = value;
+
+ if (!value)
+ ad.local_appearance = UINT16_MAX;
+
+ g_dbus_emit_property_changed(conn, AD_PATH, AD_IFACE, "Includes");
+}
+
+void ad_advertise_local_appearance(DBusConnection *conn, uint16_t value)
+{
+ if (ad.local_appearance == value)
+ return;
+
+ ad.local_appearance = value;
+
+ g_dbus_emit_property_changed(conn, AD_PATH, AD_IFACE, "Appearance");
+}
+
+void ad_advertise_duration(DBusConnection *conn, uint16_t value)
+{
+ if (ad.duration == value)
+ return;
+
+ ad.duration = value;
+
+ g_dbus_emit_property_changed(conn, AD_PATH, AD_IFACE, "Duration");
+}
+
+void ad_advertise_timeout(DBusConnection *conn, uint16_t value)
+{
+ if (ad.timeout == value)
+ return;
+
+ ad.timeout = value;
+
+ g_dbus_emit_property_changed(conn, AD_PATH, AD_IFACE, "Timeout");
}
void ad_register(DBusConnection *conn, GDBusProxy *manager, const char *type);
void ad_unregister(DBusConnection *conn, GDBusProxy *manager);
-void ad_advertise_uuids(const char *arg);
-void ad_advertise_service(const char *arg);
-void ad_advertise_manufacturer(const char *arg);
-void ad_advertise_tx_power(gboolean value);
+void ad_advertise_uuids(DBusConnection *conn, int argc, char *argv[]);
+void ad_advertise_service(DBusConnection *conn, int argc, char *argv[]);
+void ad_advertise_manufacturer(DBusConnection *conn, int argc, char *argv[]);
+void ad_advertise_tx_power(DBusConnection *conn, bool value);
+void ad_advertise_name(DBusConnection *conn, bool value);
+void ad_advertise_appearance(DBusConnection *conn, bool value);
+void ad_advertise_local_name(DBusConnection *conn, const char *name);
+void ad_advertise_local_appearance(DBusConnection *conn, uint16_t value);
+void ad_advertise_duration(DBusConnection *conn, uint16_t value);
+void ad_advertise_timeout(DBusConnection *conn, uint16_t value);
#include <stdio.h>
#include <stdlib.h>
-#include <readline/readline.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <string.h>
+#include <glib.h>
+
+#include "src/shared/shell.h"
#include "gdbus/gdbus.h"
-#include "display.h"
#include "agent.h"
#define AGENT_PATH "/org/bluez/agent"
static gboolean agent_registered = FALSE;
static const char *agent_capability = NULL;
static DBusMessage *pending_message = NULL;
-static char *agent_saved_prompt = NULL;
-static int agent_saved_point = 0;
-
-static void agent_prompt(const char *msg)
-{
- char *prompt;
-
- /* Normal use should not prompt for user input to the agent a second
- * time before it releases the prompt, but we take a safe action. */
- if (agent_saved_prompt)
- return;
-
- agent_saved_point = rl_point;
- agent_saved_prompt = g_strdup(rl_prompt);
-
- rl_set_prompt("");
- rl_redisplay();
-
- prompt = g_strdup_printf(AGENT_PROMPT "%s", msg);
- rl_set_prompt(prompt);
- g_free(prompt);
-
- rl_replace_line("", 0);
- rl_redisplay();
-}
static void agent_release_prompt(void)
{
- if (!agent_saved_prompt)
+ if (!pending_message)
return;
- /* This will cause rl_expand_prompt to re-run over the last prompt, but
- * our prompt doesn't expand anyway. */
- rl_set_prompt(agent_saved_prompt);
- rl_replace_line("", 0);
- rl_point = agent_saved_point;
- rl_redisplay();
-
- g_free(agent_saved_prompt);
- agent_saved_prompt = NULL;
+ bt_shell_release_prompt("");
}
dbus_bool_t agent_completion(void)
return TRUE;
}
-static void pincode_response(DBusConnection *conn, const char *input)
+static void pincode_response(const char *input, void *user_data)
{
+ DBusConnection *conn = user_data;
+
g_dbus_send_reply(conn, pending_message, DBUS_TYPE_STRING, &input,
DBUS_TYPE_INVALID);
}
-static void passkey_response(DBusConnection *conn, const char *input)
+static void passkey_response(const char *input, void *user_data)
{
+ DBusConnection *conn = user_data;
dbus_uint32_t passkey;
+
if (sscanf(input, "%u", &passkey) == 1)
g_dbus_send_reply(conn, pending_message, DBUS_TYPE_UINT32,
&passkey, DBUS_TYPE_INVALID);
"org.bluez.Error.Canceled", NULL);
}
-static void confirm_response(DBusConnection *conn, const char *input)
+static void confirm_response(const char *input, void *user_data)
{
+ DBusConnection *conn = user_data;
+
if (!strcmp(input, "yes"))
g_dbus_send_reply(conn, pending_message, DBUS_TYPE_INVALID);
else if (!strcmp(input, "no"))
"org.bluez.Error.Canceled", NULL);
}
-dbus_bool_t agent_input(DBusConnection *conn, const char *input)
-{
- const char *member;
-
- if (!pending_message)
- return FALSE;
-
- agent_release_prompt();
-
- member = dbus_message_get_member(pending_message);
-
- if (!strcmp(member, "RequestPinCode"))
- pincode_response(conn, input);
- else if (!strcmp(member, "RequestPasskey"))
- passkey_response(conn, input);
- else if (!strcmp(member, "RequestConfirmation"))
- confirm_response(conn, input);
- else if (!strcmp(member, "RequestAuthorization"))
- confirm_response(conn, input);
- else if (!strcmp(member, "AuthorizeService"))
- confirm_response(conn, input);
- else
- g_dbus_send_error(conn, pending_message,
- "org.bluez.Error.Canceled", NULL);
-
- dbus_message_unref(pending_message);
- pending_message = NULL;
-
- return TRUE;
-}
-
static void agent_release(DBusConnection *conn)
{
agent_registered = FALSE;
static DBusMessage *release_agent(DBusConnection *conn,
DBusMessage *msg, void *user_data)
{
- rl_printf("Agent released\n");
+ bt_shell_printf("Agent released\n");
agent_release(conn);
{
const char *device;
- rl_printf("Request PIN code\n");
+ bt_shell_printf("Request PIN code\n");
dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &device,
DBUS_TYPE_INVALID);
- agent_prompt("Enter PIN code: ");
+ bt_shell_prompt_input("agent", "Enter PIN code:", pincode_response,
+ conn);
pending_message = dbus_message_ref(msg);
dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &device,
DBUS_TYPE_STRING, &pincode, DBUS_TYPE_INVALID);
- rl_printf(AGENT_PROMPT "PIN code: %s\n", pincode);
+ bt_shell_printf(AGENT_PROMPT "PIN code: %s\n", pincode);
return dbus_message_new_method_return(msg);
}
{
const char *device;
- rl_printf("Request passkey\n");
+ bt_shell_printf("Request passkey\n");
dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &device,
DBUS_TYPE_INVALID);
- agent_prompt("Enter passkey (number in 0-999999): ");
+ bt_shell_prompt_input("agent", "Enter passkey (number in 0-999999):",
+ passkey_response, conn);
pending_message = dbus_message_ref(msg);
if (entered > strlen(passkey_full))
entered = strlen(passkey_full);
- rl_printf(AGENT_PROMPT "Passkey: "
+ bt_shell_printf(AGENT_PROMPT "Passkey: "
COLOR_BOLDGRAY "%.*s" COLOR_BOLDWHITE "%s\n" COLOR_OFF,
entered, passkey_full, passkey_full + entered);
dbus_uint32_t passkey;
char *str;
- rl_printf("Request confirmation\n");
+ bt_shell_printf("Request confirmation\n");
dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &device,
DBUS_TYPE_UINT32, &passkey, DBUS_TYPE_INVALID);
- str = g_strdup_printf("Confirm passkey %06u (yes/no): ", passkey);
- agent_prompt(str);
+ str = g_strdup_printf("Confirm passkey %06u (yes/no):", passkey);
+ bt_shell_prompt_input("agent", str, confirm_response, conn);
g_free(str);
pending_message = dbus_message_ref(msg);
{
const char *device;
- rl_printf("Request authorization\n");
+ bt_shell_printf("Request authorization\n");
dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &device,
DBUS_TYPE_INVALID);
- agent_prompt("Accept pairing (yes/no): ");
+ bt_shell_prompt_input("agent", "Accept pairing (yes/no):",
+ confirm_response, conn);
pending_message = dbus_message_ref(msg);
const char *device, *uuid;
char *str;
- rl_printf("Authorize service\n");
+ bt_shell_printf("Authorize service\n");
dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &device,
DBUS_TYPE_STRING, &uuid, DBUS_TYPE_INVALID);
- str = g_strdup_printf("Authorize service %s (yes/no): ", uuid);
- agent_prompt(str);
+ str = g_strdup_printf("Authorize service %s (yes/no):", uuid);
+ bt_shell_prompt_input("agent", str, confirm_response, conn);
g_free(str);
pending_message = dbus_message_ref(msg);
static DBusMessage *cancel_request(DBusConnection *conn,
DBusMessage *msg, void *user_data)
{
- rl_printf("Request canceled\n");
+ bt_shell_printf("Request canceled\n");
agent_release_prompt();
dbus_message_unref(pending_message);
if (dbus_set_error_from_message(&error, message) == FALSE) {
agent_registered = TRUE;
- rl_printf("Agent registered\n");
+ bt_shell_printf("Agent registered\n");
} else {
- rl_printf("Failed to register agent: %s\n", error.name);
+ bt_shell_printf("Failed to register agent: %s\n", error.name);
dbus_error_free(&error);
if (g_dbus_unregister_interface(conn, AGENT_PATH,
AGENT_INTERFACE) == FALSE)
- rl_printf("Failed to unregister agent object\n");
+ bt_shell_printf("Failed to unregister agent object\n");
}
}
{
if (agent_registered == TRUE) {
- rl_printf("Agent is already registered\n");
+ bt_shell_printf("Agent is already registered\n");
return;
}
if (g_dbus_register_interface(conn, AGENT_PATH,
AGENT_INTERFACE, methods,
NULL, NULL, NULL, NULL) == FALSE) {
- rl_printf("Failed to register agent object\n");
+ bt_shell_printf("Failed to register agent object\n");
return;
}
register_agent_setup,
register_agent_reply,
conn, NULL) == FALSE) {
- rl_printf("Failed to call register agent method\n");
+ bt_shell_printf("Failed to call register agent method\n");
return;
}
dbus_error_init(&error);
if (dbus_set_error_from_message(&error, message) == FALSE) {
- rl_printf("Agent unregistered\n");
+ bt_shell_printf("Agent unregistered\n");
agent_release(conn);
} else {
- rl_printf("Failed to unregister agent: %s\n", error.name);
+ bt_shell_printf("Failed to unregister agent: %s\n", error.name);
dbus_error_free(&error);
}
}
void agent_unregister(DBusConnection *conn, GDBusProxy *manager)
{
if (agent_registered == FALSE) {
- rl_printf("No agent is registered\n");
+ bt_shell_printf("No agent is registered\n");
return;
}
if (!manager) {
- rl_printf("Agent unregistered\n");
+ bt_shell_printf("Agent unregistered\n");
agent_release(conn);
return;
}
unregister_agent_setup,
unregister_agent_reply,
conn, NULL) == FALSE) {
- rl_printf("Failed to call unregister agent method\n");
+ bt_shell_printf("Failed to call unregister agent method\n");
return;
}
}
dbus_error_init(&error);
if (dbus_set_error_from_message(&error, message) == TRUE) {
- rl_printf("Failed to request default agent: %s\n", error.name);
+ bt_shell_printf("Failed to request default agent: %s\n",
+ error.name);
dbus_error_free(&error);
return;
}
- rl_printf("Default agent request successful\n");
+ bt_shell_printf("Default agent request successful\n");
}
void agent_default(DBusConnection *conn, GDBusProxy *manager)
{
if (agent_registered == FALSE) {
- rl_printf("No agent is registered\n");
+ bt_shell_printf("No agent is registered\n");
return;
}
request_default_setup,
request_default_reply,
NULL, NULL) == FALSE) {
- rl_printf("Failed to call request default agent method\n");
+ bt_shell_printf("Failed to call RequestDefaultAgent method\n");
return;
}
}
void agent_default(DBusConnection *conn, GDBusProxy *manager);
dbus_bool_t agent_completion(void);
-dbus_bool_t agent_input(DBusConnection *conn, const char *input);
#include "display.h"
+static char *saved_prompt = NULL;
+static int saved_point = 0;
+static rl_prompt_input_func saved_func = NULL;
+static void *saved_user_data = NULL;
+
void rl_printf(const char *fmt, ...)
{
va_list args;
rl_printf("%s\n", str);
}
}
+
+void rl_prompt_input(const char *label, const char *msg,
+ rl_prompt_input_func func, void *user_data)
+{
+ char prompt[256];
+
+ /* Normal use should not prompt for user input to the value a second
+ * time before it releases the prompt, but we take a safe action. */
+ if (saved_prompt)
+ return;
+
+ saved_point = rl_point;
+ saved_prompt = strdup(rl_prompt);
+ saved_func = func;
+ saved_user_data = user_data;
+
+ rl_set_prompt("");
+ rl_redisplay();
+
+ memset(prompt, 0, sizeof(prompt));
+ snprintf(prompt, sizeof(prompt), COLOR_RED "[%s]" COLOR_OFF " %s ",
+ label, msg);
+ rl_set_prompt(prompt);
+
+ rl_replace_line("", 0);
+ rl_redisplay();
+}
+
+int rl_release_prompt(const char *input)
+{
+ rl_prompt_input_func func;
+ void *user_data;
+
+ if (!saved_prompt)
+ return -1;
+
+ /* This will cause rl_expand_prompt to re-run over the last prompt, but
+ * our prompt doesn't expand anyway. */
+ rl_set_prompt(saved_prompt);
+ rl_replace_line("", 0);
+ rl_point = saved_point;
+ rl_redisplay();
+
+ free(saved_prompt);
+ saved_prompt = NULL;
+
+ func = saved_func;
+ user_data = saved_user_data;
+
+ saved_func = NULL;
+ saved_user_data = NULL;
+
+ func(input, user_data);
+
+ return 0;
+}
void rl_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
void rl_hexdump(const unsigned char *buf, size_t len);
+
+typedef void (*rl_prompt_input_func) (const char *input, void *user_data);
+void rl_prompt_input(const char *label, const char *msg,
+ rl_prompt_input_func func, void *user_data);
+int rl_release_prompt(const char *input);
#include <stdlib.h>
#include <stdbool.h>
#include <sys/uio.h>
-#include <wordexp.h>
+#include <fcntl.h>
+#include <string.h>
-#include <readline/readline.h>
-#include <readline/history.h>
#include <glib.h>
+#include "src/shared/util.h"
+#include "src/shared/queue.h"
+#include "src/shared/io.h"
+#include "src/shared/shell.h"
#include "gdbus/gdbus.h"
-#include "monitor/uuid.h"
-#include "display.h"
#include "gatt.h"
-#define PROFILE_PATH "/org/bluez/profile"
+#define APP_PATH "/org/bluez/app"
#define PROFILE_INTERFACE "org.bluez.GattProfile1"
+#define SERVICE_INTERFACE "org.bluez.GattService1"
+#define CHRC_INTERFACE "org.bluez.GattCharacteristic1"
+#define DESC_INTERFACE "org.bluez.GattDescriptor1"
/* String display constants */
#define COLORED_NEW COLOR_GREEN "NEW" COLOR_OFF
#define COLORED_CHG COLOR_YELLOW "CHG" COLOR_OFF
#define COLORED_DEL COLOR_RED "DEL" COLOR_OFF
+struct desc {
+ struct chrc *chrc;
+ char *path;
+ char *uuid;
+ char **flags;
+ int value_len;
+ uint8_t *value;
+};
+
+struct chrc {
+ struct service *service;
+ char *path;
+ char *uuid;
+ char **flags;
+ bool notifying;
+ GList *descs;
+ int value_len;
+ uint8_t *value;
+ uint16_t mtu;
+ struct io *write_io;
+ struct io *notify_io;
+};
+
+struct service {
+ DBusConnection *conn;
+ char *path;
+ char *uuid;
+ bool primary;
+ GList *chrcs;
+};
+
+static GList *local_services;
static GList *services;
static GList *characteristics;
static GList *descriptors;
static GList *managers;
+static GList *uuids;
+
+struct pipe_io {
+ GDBusProxy *proxy;
+ struct io *io;
+ uint16_t mtu;
+};
+
+static struct pipe_io write_io;
+static struct pipe_io notify_io;
+
+static void print_service(struct service *service, const char *description)
+{
+ const char *text;
+
+ text = bt_uuidstr_to_str(service->uuid);
+ if (!text)
+ bt_shell_printf("%s%s%s%s Service\n\t%s\n\t%s\n",
+ description ? "[" : "",
+ description ? : "",
+ description ? "] " : "",
+ service->primary ? "Primary" :
+ "Secondary",
+ service->path, service->uuid);
+ else
+ bt_shell_printf("%s%s%s%s Service\n\t%s\n\t%s\n\t%s\n",
+ description ? "[" : "",
+ description ? : "",
+ description ? "] " : "",
+ service->primary ? "Primary" :
+ "Secondary",
+ service->path, service->uuid, text);
+}
-static void print_service(GDBusProxy *proxy, const char *description)
+static void print_service_proxy(GDBusProxy *proxy, const char *description)
{
+ struct service service;
DBusMessageIter iter;
- const char *uuid, *text;
+ const char *uuid;
dbus_bool_t primary;
if (g_dbus_proxy_get_property(proxy, "UUID", &iter) == FALSE)
dbus_message_iter_get_basic(&iter, &primary);
- text = uuidstr_to_str(uuid);
- if (!text)
- rl_printf("%s%s%s%s Service\n\t%s\n\t%s\n",
- description ? "[" : "",
- description ? : "",
- description ? "] " : "",
- primary ? "Primary" : "Secondary",
- g_dbus_proxy_get_path(proxy),
- uuid);
- else
- rl_printf("%s%s%s%s Service\n\t%s\n\t%s\n\t%s\n",
- description ? "[" : "",
- description ? : "",
- description ? "] " : "",
- primary ? "Primary" : "Secondary",
- g_dbus_proxy_get_path(proxy),
- uuid, text);
+ service.path = (char *) g_dbus_proxy_get_path(proxy);
+ service.uuid = (char *) uuid;
+ service.primary = primary;
+
+ print_service(&service, description);
}
void gatt_add_service(GDBusProxy *proxy)
{
services = g_list_append(services, proxy);
- print_service(proxy, COLORED_NEW);
+ print_service_proxy(proxy, COLORED_NEW);
}
void gatt_remove_service(GDBusProxy *proxy)
services = g_list_delete_link(services, l);
- print_service(proxy, COLORED_DEL);
+ print_service_proxy(proxy, COLORED_DEL);
}
-static void print_characteristic(GDBusProxy *proxy, const char *description)
+static void print_chrc(struct chrc *chrc, const char *description)
{
- DBusMessageIter iter;
- const char *uuid, *text;
-
- if (g_dbus_proxy_get_property(proxy, "UUID", &iter) == FALSE)
- return;
-
- dbus_message_iter_get_basic(&iter, &uuid);
+ const char *text;
- text = uuidstr_to_str(uuid);
+ text = bt_uuidstr_to_str(chrc->uuid);
if (!text)
- rl_printf("%s%s%sCharacteristic\n\t%s\n\t%s\n",
+ bt_shell_printf("%s%s%sCharacteristic\n\t%s\n\t%s\n",
description ? "[" : "",
description ? : "",
description ? "] " : "",
- g_dbus_proxy_get_path(proxy),
- uuid);
+ chrc->path, chrc->uuid);
else
- rl_printf("%s%s%sCharacteristic\n\t%s\n\t%s\n\t%s\n",
+ bt_shell_printf("%s%s%sCharacteristic\n\t%s\n\t%s\n\t%s\n",
description ? "[" : "",
description ? : "",
description ? "] " : "",
- g_dbus_proxy_get_path(proxy),
- uuid, text);
+ chrc->path, chrc->uuid, text);
+}
+
+static void print_characteristic(GDBusProxy *proxy, const char *description)
+{
+ struct chrc chrc;
+ DBusMessageIter iter;
+ const char *uuid;
+
+ if (g_dbus_proxy_get_property(proxy, "UUID", &iter) == FALSE)
+ return;
+
+ dbus_message_iter_get_basic(&iter, &uuid);
+
+ chrc.path = (char *) g_dbus_proxy_get_path(proxy);
+ chrc.uuid = (char *) uuid;
+
+ print_chrc(&chrc, description);
}
-static gboolean characteristic_is_child(GDBusProxy *characteristic)
+static gboolean chrc_is_child(GDBusProxy *characteristic)
{
GList *l;
DBusMessageIter iter;
void gatt_add_characteristic(GDBusProxy *proxy)
{
- if (!characteristic_is_child(proxy))
+ if (!chrc_is_child(proxy))
return;
characteristics = g_list_append(characteristics, proxy);
print_characteristic(proxy, COLORED_NEW);
}
+static void notify_io_destroy(void)
+{
+ io_destroy(notify_io.io);
+ memset(¬ify_io, 0, sizeof(notify_io));
+}
+
+static void write_io_destroy(void)
+{
+ io_destroy(write_io.io);
+ memset(&write_io, 0, sizeof(write_io));
+}
+
void gatt_remove_characteristic(GDBusProxy *proxy)
{
GList *l;
characteristics = g_list_delete_link(characteristics, l);
print_characteristic(proxy, COLORED_DEL);
+
+ if (write_io.proxy == proxy)
+ write_io_destroy();
+ else if (notify_io.proxy == proxy)
+ notify_io_destroy();
}
-static void print_descriptor(GDBusProxy *proxy, const char *description)
+static void print_desc(struct desc *desc, const char *description)
{
- DBusMessageIter iter;
- const char *uuid, *text;
-
- if (g_dbus_proxy_get_property(proxy, "UUID", &iter) == FALSE)
- return;
+ const char *text;
- dbus_message_iter_get_basic(&iter, &uuid);
-
- text = uuidstr_to_str(uuid);
+ text = bt_uuidstr_to_str(desc->uuid);
if (!text)
- rl_printf("%s%s%sDescriptor\n\t%s\n\t%s\n",
+ bt_shell_printf("%s%s%sDescriptor\n\t%s\n\t%s\n",
description ? "[" : "",
description ? : "",
description ? "] " : "",
- g_dbus_proxy_get_path(proxy),
- uuid);
+ desc->path, desc->uuid);
else
- rl_printf("%s%s%sDescriptor\n\t%s\n\t%s\n\t%s\n",
+ bt_shell_printf("%s%s%sDescriptor\n\t%s\n\t%s\n\t%s\n",
description ? "[" : "",
description ? : "",
description ? "] " : "",
- g_dbus_proxy_get_path(proxy),
- uuid, text);
+ desc->path, desc->uuid, text);
+}
+
+static void print_descriptor(GDBusProxy *proxy, const char *description)
+{
+ struct desc desc;
+ DBusMessageIter iter;
+ const char *uuid;
+
+ if (g_dbus_proxy_get_property(proxy, "UUID", &iter) == FALSE)
+ return;
+
+ dbus_message_iter_get_basic(&iter, &uuid);
+
+ desc.path = (char *) g_dbus_proxy_get_path(proxy);
+ desc.uuid = (char *) uuid;
+
+ print_desc(&desc, description);
}
static gboolean descriptor_is_child(GDBusProxy *characteristic)
continue;
if (source == services) {
- print_service(proxy, NULL);
+ print_service_proxy(proxy, NULL);
list_attributes(proxy_path, characteristics);
} else if (source == characteristics) {
print_characteristic(proxy, NULL);
return NULL;
}
-GDBusProxy *gatt_select_attribute(const char *path)
+static GDBusProxy *select_attribute(const char *path)
{
GDBusProxy *proxy;
return select_proxy(path, descriptors);
}
+static GDBusProxy *select_proxy_by_uuid(GDBusProxy *parent, const char *uuid,
+ GList *source)
+{
+ GList *l;
+ const char *value;
+ DBusMessageIter iter;
+
+ for (l = source; l; l = g_list_next(l)) {
+ GDBusProxy *proxy = l->data;
+
+ if (parent && !g_str_has_prefix(g_dbus_proxy_get_path(proxy),
+ g_dbus_proxy_get_path(parent)))
+ continue;
+
+ if (g_dbus_proxy_get_property(proxy, "UUID", &iter) == FALSE)
+ continue;
+
+ dbus_message_iter_get_basic(&iter, &value);
+
+ if (strcasecmp(uuid, value) == 0)
+ return proxy;
+ }
+
+ return NULL;
+}
+
+static GDBusProxy *select_attribute_by_uuid(GDBusProxy *parent,
+ const char *uuid)
+{
+ GDBusProxy *proxy;
+
+ proxy = select_proxy_by_uuid(parent, uuid, services);
+ if (proxy)
+ return proxy;
+
+ proxy = select_proxy_by_uuid(parent, uuid, characteristics);
+ if (proxy)
+ return proxy;
+
+ return select_proxy_by_uuid(parent, uuid, descriptors);
+}
+
+GDBusProxy *gatt_select_attribute(GDBusProxy *parent, const char *arg)
+{
+ if (arg[0] == '/')
+ return select_attribute(arg);
+
+ if (parent) {
+ GDBusProxy *proxy = select_attribute_by_uuid(parent, arg);
+ if (proxy)
+ return proxy;
+ }
+
+ return select_attribute_by_uuid(parent, arg);
+}
+
static char *attribute_generator(const char *text, int state, GList *source)
{
static int index, len;
dbus_error_init(&error);
if (dbus_set_error_from_message(&error, message) == TRUE) {
- rl_printf("Failed to read: %s\n", error.name);
+ bt_shell_printf("Failed to read: %s\n", error.name);
dbus_error_free(&error);
return;
}
dbus_message_iter_init(message, &iter);
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
- rl_printf("Invalid response to read\n");
+ bt_shell_printf("Invalid response to read\n");
return;
}
dbus_message_iter_get_fixed_array(&array, &value, &len);
if (len < 0) {
- rl_printf("Unable to parse value\n");
+ bt_shell_printf("Unable to parse value\n");
return;
}
- rl_hexdump(value, len);
+ bt_shell_hexdump(value, len);
}
static void read_setup(DBusMessageIter *iter, void *user_data)
{
if (g_dbus_proxy_method_call(proxy, "ReadValue", read_setup, read_reply,
NULL, NULL) == FALSE) {
- rl_printf("Failed to read\n");
+ bt_shell_printf("Failed to read\n");
return;
}
- rl_printf("Attempting to read %s\n", g_dbus_proxy_get_path(proxy));
+ bt_shell_printf("Attempting to read %s\n", g_dbus_proxy_get_path(proxy));
}
void gatt_read_attribute(GDBusProxy *proxy)
return;
}
- rl_printf("Unable to read attribute %s\n",
+ bt_shell_printf("Unable to read attribute %s\n",
g_dbus_proxy_get_path(proxy));
}
dbus_error_init(&error);
if (dbus_set_error_from_message(&error, message) == TRUE) {
- rl_printf("Failed to write: %s\n", error.name);
+ bt_shell_printf("Failed to write: %s\n", error.name);
dbus_error_free(&error);
return;
}
continue;
if (i >= G_N_ELEMENTS(value)) {
- rl_printf("Too much data\n");
+ bt_shell_printf("Too much data\n");
return;
}
val = strtol(entry, &endptr, 0);
if (!endptr || *endptr != '\0' || val > UINT8_MAX) {
- rl_printf("Invalid value at index %d\n", i);
+ bt_shell_printf("Invalid value at index %d\n", i);
return;
}
iov.iov_base = value;
iov.iov_len = i;
+ /* Write using the fd if it has been acquired and fit the MTU */
+ if (proxy == write_io.proxy && (write_io.io && write_io.mtu >= i)) {
+ bt_shell_printf("Attempting to write fd %d\n",
+ io_get_fd(write_io.io));
+ if (io_send(write_io.io, &iov, 1) < 0) {
+ bt_shell_printf("Failed to write: %s", strerror(errno));
+ return;
+ }
+ return;
+ }
+
if (g_dbus_proxy_method_call(proxy, "WriteValue", write_setup,
write_reply, &iov, NULL) == FALSE) {
- rl_printf("Failed to write\n");
+ bt_shell_printf("Failed to write\n");
return;
}
- rl_printf("Attempting to write %s\n", g_dbus_proxy_get_path(proxy));
+ bt_shell_printf("Attempting to write %s\n", g_dbus_proxy_get_path(proxy));
}
void gatt_write_attribute(GDBusProxy *proxy, const char *arg)
return;
}
- rl_printf("Unable to write attribute %s\n",
+ bt_shell_printf("Unable to write attribute %s\n",
+ g_dbus_proxy_get_path(proxy));
+}
+
+static bool pipe_read(struct io *io, void *user_data)
+{
+ struct chrc *chrc = user_data;
+ uint8_t buf[512];
+ int fd = io_get_fd(io);
+ ssize_t bytes_read;
+
+ if (io != notify_io.io && !chrc)
+ return true;
+
+ bytes_read = read(fd, buf, sizeof(buf));
+ if (bytes_read < 0)
+ return false;
+
+ if (chrc)
+ bt_shell_printf("[" COLORED_CHG "] Attribute %s written:\n",
+ chrc->path);
+ else
+ bt_shell_printf("[" COLORED_CHG "] %s Notification:\n",
+ g_dbus_proxy_get_path(notify_io.proxy));
+
+ bt_shell_hexdump(buf, bytes_read);
+
+ return true;
+}
+
+static bool pipe_hup(struct io *io, void *user_data)
+{
+ struct chrc *chrc = user_data;
+
+ if (chrc) {
+ bt_shell_printf("Attribute %s Write pipe closed\n", chrc->path);
+ if (chrc->write_io) {
+ io_destroy(chrc->write_io);
+ chrc->write_io = NULL;
+ }
+ return false;
+ }
+
+ bt_shell_printf("%s closed\n", io == notify_io.io ? "Notify" : "Write");
+
+ if (io == notify_io.io)
+ notify_io_destroy();
+ else
+ write_io_destroy();
+
+ return false;
+}
+
+static struct io *pipe_io_new(int fd, void *user_data)
+{
+ struct io *io;
+
+ io = io_new(fd);
+
+ io_set_close_on_destroy(io, true);
+
+ io_set_read_handler(io, pipe_read, user_data, NULL);
+
+ io_set_disconnect_handler(io, pipe_hup, user_data, NULL);
+
+ return io;
+}
+
+static void acquire_write_reply(DBusMessage *message, void *user_data)
+{
+ DBusError error;
+ int fd;
+
+ dbus_error_init(&error);
+
+ if (dbus_set_error_from_message(&error, message) == TRUE) {
+ bt_shell_printf("Failed to acquire write: %s\n", error.name);
+ dbus_error_free(&error);
+ write_io.proxy = NULL;
+ return;
+ }
+
+ if (write_io.io)
+ write_io_destroy();
+
+ if ((dbus_message_get_args(message, NULL, DBUS_TYPE_UNIX_FD, &fd,
+ DBUS_TYPE_UINT16, &write_io.mtu,
+ DBUS_TYPE_INVALID) == false)) {
+ bt_shell_printf("Invalid AcquireWrite response\n");
+ return;
+ }
+
+ bt_shell_printf("AcquireWrite success: fd %d MTU %u\n", fd, write_io.mtu);
+
+ write_io.io = pipe_io_new(fd, NULL);
+}
+
+static void acquire_setup(DBusMessageIter *iter, void *user_data)
+{
+ DBusMessageIter dict;
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING
+ DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+ &dict);
+
+ dbus_message_iter_close_container(iter, &dict);
+}
+
+void gatt_acquire_write(GDBusProxy *proxy, const char *arg)
+{
+ const char *iface;
+
+ iface = g_dbus_proxy_get_interface(proxy);
+ if (strcmp(iface, "org.bluez.GattCharacteristic1")) {
+ bt_shell_printf("Unable to acquire write: %s not a characteristic\n",
+ g_dbus_proxy_get_path(proxy));
+ return;
+ }
+
+ if (g_dbus_proxy_method_call(proxy, "AcquireWrite", acquire_setup,
+ acquire_write_reply, NULL, NULL) == FALSE) {
+ bt_shell_printf("Failed to AcquireWrite\n");
+ return;
+ }
+
+ write_io.proxy = proxy;
+}
+
+void gatt_release_write(GDBusProxy *proxy, const char *arg)
+{
+ if (proxy != write_io.proxy || !write_io.io) {
+ bt_shell_printf("Write not acquired\n");
+ return;
+ }
+
+ write_io_destroy();
+}
+
+static void acquire_notify_reply(DBusMessage *message, void *user_data)
+{
+ DBusError error;
+ int fd;
+
+ dbus_error_init(&error);
+
+ if (dbus_set_error_from_message(&error, message) == TRUE) {
+ bt_shell_printf("Failed to acquire notify: %s\n", error.name);
+ dbus_error_free(&error);
+ write_io.proxy = NULL;
+ return;
+ }
+
+ if (notify_io.io) {
+ io_destroy(notify_io.io);
+ notify_io.io = NULL;
+ }
+
+ notify_io.mtu = 0;
+
+ if ((dbus_message_get_args(message, NULL, DBUS_TYPE_UNIX_FD, &fd,
+ DBUS_TYPE_UINT16, ¬ify_io.mtu,
+ DBUS_TYPE_INVALID) == false)) {
+ bt_shell_printf("Invalid AcquireNotify response\n");
+ return;
+ }
+
+ bt_shell_printf("AcquireNotify success: fd %d MTU %u\n", fd, notify_io.mtu);
+
+ notify_io.io = pipe_io_new(fd, NULL);
+}
+
+void gatt_acquire_notify(GDBusProxy *proxy, const char *arg)
+{
+ const char *iface;
+
+ iface = g_dbus_proxy_get_interface(proxy);
+ if (strcmp(iface, "org.bluez.GattCharacteristic1")) {
+ bt_shell_printf("Unable to acquire notify: %s not a characteristic\n",
g_dbus_proxy_get_path(proxy));
+ return;
+ }
+
+ if (g_dbus_proxy_method_call(proxy, "AcquireNotify", acquire_setup,
+ acquire_notify_reply, NULL, NULL) == FALSE) {
+ bt_shell_printf("Failed to AcquireNotify\n");
+ return;
+ }
+
+ notify_io.proxy = proxy;
+}
+
+void gatt_release_notify(GDBusProxy *proxy, const char *arg)
+{
+ if (proxy != notify_io.proxy || !notify_io.io) {
+ bt_shell_printf("Notify not acquired\n");
+ return;
+ }
+
+ notify_io_destroy();
}
static void notify_reply(DBusMessage *message, void *user_data)
dbus_error_init(&error);
if (dbus_set_error_from_message(&error, message) == TRUE) {
- rl_printf("Failed to %s notify: %s\n",
+ bt_shell_printf("Failed to %s notify: %s\n",
enable ? "start" : "stop", error.name);
dbus_error_free(&error);
return;
}
- rl_printf("Notify %s\n", enable == TRUE ? "started" : "stopped");
+ bt_shell_printf("Notify %s\n", enable == TRUE ? "started" : "stopped");
}
static void notify_attribute(GDBusProxy *proxy, bool enable)
if (g_dbus_proxy_method_call(proxy, method, NULL, notify_reply,
GUINT_TO_POINTER(enable), NULL) == FALSE) {
- rl_printf("Failed to %s notify\n", enable ? "start" : "stop");
+ bt_shell_printf("Failed to %s notify\n", enable ? "start" : "stop");
return;
}
}
return;
}
- rl_printf("Unable to notify attribute %s\n",
+ bt_shell_printf("Unable to notify attribute %s\n",
g_dbus_proxy_get_path(proxy));
}
-static void register_profile_setup(DBusMessageIter *iter, void *user_data)
+static void register_app_setup(DBusMessageIter *iter, void *user_data)
{
- wordexp_t *w = user_data;
- DBusMessageIter uuids, opt;
- const char *path = PROFILE_PATH;
- size_t i;
+ DBusMessageIter opt;
+ const char *path = "/";
dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path);
- dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "s", &uuids);
- for (i = 0; i < w->we_wordc; i++)
- dbus_message_iter_append_basic(&uuids, DBUS_TYPE_STRING,
- &w->we_wordv[i]);
- dbus_message_iter_close_container(iter, &uuids);
-
dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
DBUS_TYPE_STRING_AS_STRING
}
-static void register_profile_reply(DBusMessage *message, void *user_data)
+static void register_app_reply(DBusMessage *message, void *user_data)
{
DBusError error;
dbus_error_init(&error);
if (dbus_set_error_from_message(&error, message) == TRUE) {
- rl_printf("Failed to register profile: %s\n", error.name);
+ bt_shell_printf("Failed to register application: %s\n", error.name);
dbus_error_free(&error);
return;
}
- rl_printf("Profile registered\n");
+ bt_shell_printf("Application registered\n");
}
void gatt_add_manager(GDBusProxy *proxy)
static DBusMessage *release_profile(DBusConnection *conn,
DBusMessage *msg, void *user_data)
{
- g_dbus_unregister_interface(conn, PROFILE_PATH, PROFILE_INTERFACE);
+ g_dbus_unregister_interface(conn, APP_PATH, PROFILE_INTERFACE);
return dbus_message_new_method_return(msg);
}
{ }
};
-void gatt_register_profile(DBusConnection *conn, GDBusProxy *proxy,
- wordexp_t *w)
+static gboolean get_uuids(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ DBusMessageIter entry;
+ GList *uuid;
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+ DBUS_TYPE_STRING_AS_STRING, &entry);
+
+ for (uuid = uuids; uuid; uuid = g_list_next(uuid->next))
+ dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING,
+ &uuid->data);
+
+ dbus_message_iter_close_container(iter, &entry);
+
+ return TRUE;
+}
+
+static const GDBusPropertyTable properties[] = {
+ { "UUIDs", "as", get_uuids },
+ { }
+};
+
+void gatt_register_app(DBusConnection *conn, GDBusProxy *proxy,
+ int argc, char *argv[])
{
GList *l;
+ int i;
l = g_list_find_custom(managers, proxy, match_proxy);
if (!l) {
- rl_printf("Unable to find GattManager proxy\n");
+ bt_shell_printf("Unable to find GattManager proxy\n");
return;
}
- if (g_dbus_register_interface(conn, PROFILE_PATH,
- PROFILE_INTERFACE, methods,
- NULL, NULL, NULL, NULL) == FALSE) {
- rl_printf("Failed to register profile object\n");
- return;
+ for (i = 0; i < argc; i++)
+ uuids = g_list_append(uuids, g_strdup(argv[i]));
+
+ if (uuids) {
+ if (g_dbus_register_interface(conn, APP_PATH,
+ PROFILE_INTERFACE, methods,
+ NULL, properties, NULL,
+ NULL) == FALSE) {
+ bt_shell_printf("Failed to register application object\n");
+ return;
+ }
}
- if (g_dbus_proxy_method_call(l->data, "RegisterProfile",
- register_profile_setup,
- register_profile_reply, w,
+ if (g_dbus_proxy_method_call(l->data, "RegisterApplication",
+ register_app_setup,
+ register_app_reply, NULL,
NULL) == FALSE) {
- rl_printf("Failed register profile\n");
+ bt_shell_printf("Failed register application\n");
+ g_dbus_unregister_interface(conn, APP_PATH, PROFILE_INTERFACE);
return;
}
}
-static void unregister_profile_reply(DBusMessage *message, void *user_data)
+static void unregister_app_reply(DBusMessage *message, void *user_data)
{
DBusConnection *conn = user_data;
DBusError error;
dbus_error_init(&error);
if (dbus_set_error_from_message(&error, message) == TRUE) {
- rl_printf("Failed to unregister profile: %s\n", error.name);
+ bt_shell_printf("Failed to unregister application: %s\n", error.name);
dbus_error_free(&error);
return;
}
- rl_printf("Profile unregistered\n");
+ bt_shell_printf("Application unregistered\n");
+
+ if (!uuids)
+ return;
+
+ g_list_free_full(uuids, g_free);
+ uuids = NULL;
- g_dbus_unregister_interface(conn, PROFILE_PATH, PROFILE_INTERFACE);
+ g_dbus_unregister_interface(conn, APP_PATH, PROFILE_INTERFACE);
}
-static void unregister_profile_setup(DBusMessageIter *iter, void *user_data)
+static void unregister_app_setup(DBusMessageIter *iter, void *user_data)
{
- const char *path = PROFILE_PATH;
+ const char *path = "/";
dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path);
}
-void gatt_unregister_profile(DBusConnection *conn, GDBusProxy *proxy)
+void gatt_unregister_app(DBusConnection *conn, GDBusProxy *proxy)
{
GList *l;
l = g_list_find_custom(managers, proxy, match_proxy);
if (!l) {
- rl_printf("Unable to find GattManager proxy\n");
+ bt_shell_printf("Unable to find GattManager proxy\n");
return;
}
- if (g_dbus_proxy_method_call(l->data, "UnregisterProfile",
- unregister_profile_setup,
- unregister_profile_reply, conn,
+ if (g_dbus_proxy_method_call(l->data, "UnregisterApplication",
+ unregister_app_setup,
+ unregister_app_reply, conn,
NULL) == FALSE) {
- rl_printf("Failed unregister profile\n");
+ bt_shell_printf("Failed unregister profile\n");
return;
}
}
+
+static void desc_free(void *data)
+{
+ struct desc *desc = data;
+
+ g_free(desc->path);
+ g_free(desc->uuid);
+ g_strfreev(desc->flags);
+ g_free(desc->value);
+ g_free(desc);
+}
+
+static void desc_unregister(void *data)
+{
+ struct desc *desc = data;
+
+ print_desc(desc, COLORED_DEL);
+
+ g_dbus_unregister_interface(desc->chrc->service->conn, desc->path,
+ DESC_INTERFACE);
+}
+
+static void chrc_free(void *data)
+{
+ struct chrc *chrc = data;
+
+ g_list_free_full(chrc->descs, desc_unregister);
+ g_free(chrc->path);
+ g_free(chrc->uuid);
+ g_strfreev(chrc->flags);
+ g_free(chrc->value);
+ g_free(chrc);
+}
+
+static void chrc_unregister(void *data)
+{
+ struct chrc *chrc = data;
+
+ print_chrc(chrc, COLORED_DEL);
+
+ g_dbus_unregister_interface(chrc->service->conn, chrc->path,
+ CHRC_INTERFACE);
+}
+
+static void service_free(void *data)
+{
+ struct service *service = data;
+
+ g_list_free_full(service->chrcs, chrc_unregister);
+ g_free(service->path);
+ g_free(service->uuid);
+ g_free(service);
+}
+
+static gboolean service_get_uuid(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct service *service = data;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &service->uuid);
+
+ return TRUE;
+}
+
+static gboolean service_get_primary(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct service *service = data;
+ dbus_bool_t primary;
+
+ primary = service->primary ? TRUE : FALSE;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &primary);
+
+ return TRUE;
+}
+
+static const GDBusPropertyTable service_properties[] = {
+ { "UUID", "s", service_get_uuid },
+ { "Primary", "b", service_get_primary },
+ { }
+};
+
+static void service_set_primary(const char *input, void *user_data)
+{
+ struct service *service = user_data;
+
+ if (!strcmp(input, "yes"))
+ service->primary = true;
+ else if (!strcmp(input, "no")) {
+ service->primary = false;
+ } else {
+ bt_shell_printf("Invalid option: %s\n", input);
+ local_services = g_list_remove(local_services, service);
+ print_service(service, COLORED_DEL);
+ g_dbus_unregister_interface(service->conn, service->path,
+ SERVICE_INTERFACE);
+ }
+}
+
+void gatt_register_service(DBusConnection *conn, GDBusProxy *proxy,
+ int argc, char *argv[])
+{
+ struct service *service;
+ bool primary = true;
+
+ service = g_new0(struct service, 1);
+ service->conn = conn;
+ service->uuid = g_strdup(argv[1]);
+ service->path = g_strdup_printf("%s/service%p", APP_PATH, service);
+ service->primary = primary;
+
+ if (g_dbus_register_interface(conn, service->path,
+ SERVICE_INTERFACE, NULL, NULL,
+ service_properties, service,
+ service_free) == FALSE) {
+ bt_shell_printf("Failed to register service object\n");
+ service_free(service);
+ return;
+ }
+
+ print_service(service, COLORED_NEW);
+
+ local_services = g_list_append(local_services, service);
+
+ bt_shell_prompt_input(service->path, "Primary (yes/no):", service_set_primary,
+ service);
+}
+
+static struct service *service_find(const char *pattern)
+{
+ GList *l;
+
+ for (l = local_services; l; l = g_list_next(l)) {
+ struct service *service = l->data;
+
+ /* match object path */
+ if (!strcmp(service->path, pattern))
+ return service;
+
+ /* match UUID */
+ if (!strcmp(service->uuid, pattern))
+ return service;
+ }
+
+ return NULL;
+}
+
+void gatt_unregister_service(DBusConnection *conn, GDBusProxy *proxy,
+ int argc, char *argv[])
+{
+ struct service *service;
+
+ service = service_find(argv[1]);
+ if (!service) {
+ bt_shell_printf("Failed to unregister service object\n");
+ return;
+ }
+
+ local_services = g_list_remove(local_services, service);
+
+ print_service(service, COLORED_DEL);
+
+ g_dbus_unregister_interface(service->conn, service->path,
+ SERVICE_INTERFACE);
+}
+
+static gboolean chrc_get_uuid(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct chrc *chrc = data;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &chrc->uuid);
+
+ return TRUE;
+}
+
+static gboolean chrc_get_service(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct chrc *chrc = data;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH,
+ &chrc->service->path);
+
+ return TRUE;
+}
+
+static gboolean chrc_get_value(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct chrc *chrc = data;
+ DBusMessageIter array;
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "y", &array);
+
+ dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE,
+ &chrc->value, chrc->value_len);
+
+ dbus_message_iter_close_container(iter, &array);
+
+ return TRUE;
+}
+
+static gboolean chrc_get_notifying(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct chrc *chrc = data;
+ dbus_bool_t value;
+
+ value = chrc->notifying ? TRUE : FALSE;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &value);
+
+ return TRUE;
+}
+
+static gboolean chrc_get_flags(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct chrc *chrc = data;
+ int i;
+ DBusMessageIter array;
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "s", &array);
+
+ for (i = 0; chrc->flags[i]; i++)
+ dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING,
+ &chrc->flags[i]);
+
+ dbus_message_iter_close_container(iter, &array);
+
+ return TRUE;
+}
+
+static gboolean chrc_get_write_acquired(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct chrc *chrc = data;
+ dbus_bool_t value;
+
+ value = chrc->write_io ? TRUE : FALSE;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &value);
+
+ return TRUE;
+}
+
+static gboolean chrc_write_acquired_exists(const GDBusPropertyTable *property,
+ void *data)
+{
+ struct chrc *chrc = data;
+ int i;
+
+ for (i = 0; chrc->flags[i]; i++) {
+ if (!strcmp("write-without-response", chrc->flags[i]))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean chrc_get_notify_acquired(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct chrc *chrc = data;
+ dbus_bool_t value;
+
+ value = chrc->notify_io ? TRUE : FALSE;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &value);
+
+ return TRUE;
+}
+
+static gboolean chrc_notify_acquired_exists(const GDBusPropertyTable *property,
+ void *data)
+{
+ struct chrc *chrc = data;
+ int i;
+
+ for (i = 0; chrc->flags[i]; i++) {
+ if (!strcmp("notify", chrc->flags[i]))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static const GDBusPropertyTable chrc_properties[] = {
+ { "UUID", "s", chrc_get_uuid, NULL, NULL },
+ { "Service", "o", chrc_get_service, NULL, NULL },
+ { "Value", "ay", chrc_get_value, NULL, NULL },
+ { "Notifying", "b", chrc_get_notifying, NULL, NULL },
+ { "Flags", "as", chrc_get_flags, NULL, NULL },
+ { "WriteAcquired", "b", chrc_get_write_acquired, NULL,
+ chrc_write_acquired_exists },
+ { "NotifyAcquired", "b", chrc_get_notify_acquired, NULL,
+ chrc_notify_acquired_exists },
+ { }
+};
+
+static DBusMessage *read_value(DBusMessage *msg, uint8_t *value,
+ uint16_t value_len)
+{
+ DBusMessage *reply;
+ DBusMessageIter iter, array;
+
+ reply = g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+
+ dbus_message_iter_init_append(reply, &iter);
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "y", &array);
+ dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE,
+ &value, value_len);
+ dbus_message_iter_close_container(&iter, &array);
+
+ return reply;
+}
+
+static DBusMessage *chrc_read_value(DBusConnection *conn, DBusMessage *msg,
+ void *user_data)
+{
+ struct chrc *chrc = user_data;
+
+ return read_value(msg, chrc->value, chrc->value_len);
+}
+
+static int parse_value_arg(DBusMessageIter *iter, uint8_t **value, int *len)
+{
+ DBusMessageIter array;
+
+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
+ return -EINVAL;
+
+ dbus_message_iter_recurse(iter, &array);
+ dbus_message_iter_get_fixed_array(&array, value, len);
+
+ return 0;
+}
+
+static DBusMessage *chrc_write_value(DBusConnection *conn, DBusMessage *msg,
+ void *user_data)
+{
+ struct chrc *chrc = user_data;
+ DBusMessageIter iter;
+
+ dbus_message_iter_init(msg, &iter);
+
+ if (parse_value_arg(&iter, &chrc->value, &chrc->value_len))
+ return g_dbus_create_error(msg,
+ "org.bluez.Error.InvalidArguments",
+ NULL);
+
+ bt_shell_printf("[" COLORED_CHG "] Attribute %s written" , chrc->path);
+
+ g_dbus_emit_property_changed(conn, chrc->path, CHRC_INTERFACE, "Value");
+
+ return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static int parse_options(DBusMessageIter *iter, struct chrc *chrc)
+{
+ DBusMessageIter dict;
+
+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
+ return -EINVAL;
+
+ dbus_message_iter_recurse(iter, &dict);
+
+ while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+ const char *key;
+ DBusMessageIter value, entry;
+ int var;
+
+ dbus_message_iter_recurse(&dict, &entry);
+ dbus_message_iter_get_basic(&entry, &key);
+
+ dbus_message_iter_next(&entry);
+ dbus_message_iter_recurse(&entry, &value);
+
+ var = dbus_message_iter_get_arg_type(&value);
+ if (strcasecmp(key, "Device") == 0) {
+ if (var != DBUS_TYPE_OBJECT_PATH)
+ return -EINVAL;
+ } else if (strcasecmp(key, "MTU") == 0) {
+ if (var != DBUS_TYPE_UINT16)
+ return -EINVAL;
+ dbus_message_iter_get_basic(&value, &chrc->mtu);
+ }
+
+ dbus_message_iter_next(&dict);
+ }
+
+ return 0;
+}
+
+static DBusMessage *chrc_create_pipe(struct chrc *chrc, DBusMessage *msg)
+{
+ int pipefd[2];
+ struct io *io;
+ bool dir;
+ DBusMessage *reply;
+
+ if (pipe2(pipefd, O_DIRECT | O_NONBLOCK | O_CLOEXEC) < 0)
+ return g_dbus_create_error(msg, "org.bluez.Error.Failed", "%s",
+ strerror(errno));
+
+ dir = dbus_message_has_member(msg, "AcquireWrite");
+
+ io = pipe_io_new(pipefd[!dir], chrc);
+ if (!io) {
+ close(pipefd[0]);
+ close(pipefd[1]);
+ return g_dbus_create_error(msg, "org.bluez.Error.Failed", "%s",
+ strerror(errno));
+ }
+
+ reply = g_dbus_create_reply(msg, DBUS_TYPE_UNIX_FD, &pipefd[dir],
+ DBUS_TYPE_UINT16, &chrc->mtu,
+ DBUS_TYPE_INVALID);
+
+ close(pipefd[dir]);
+
+ if (dir)
+ chrc->write_io = io;
+ else
+ chrc->notify_io = io;
+
+ bt_shell_printf("[" COLORED_CHG "] Attribute %s %s pipe acquired\n",
+ chrc->path, dir ? "Write" : "Notify");
+
+ return reply;
+}
+
+static DBusMessage *chrc_acquire_write(DBusConnection *conn, DBusMessage *msg,
+ void *user_data)
+{
+ struct chrc *chrc = user_data;
+ DBusMessageIter iter;
+ DBusMessage *reply;
+
+ dbus_message_iter_init(msg, &iter);
+
+ if (chrc->write_io)
+ return g_dbus_create_error(msg,
+ "org.bluez.Error.NotPermitted",
+ NULL);
+
+ if (parse_options(&iter, chrc))
+ return g_dbus_create_error(msg,
+ "org.bluez.Error.InvalidArguments",
+ NULL);
+
+ reply = chrc_create_pipe(chrc, msg);
+
+ if (chrc->write_io)
+ g_dbus_emit_property_changed(conn, chrc->path, CHRC_INTERFACE,
+ "WriteAcquired");
+
+ return reply;
+}
+
+static DBusMessage *chrc_acquire_notify(DBusConnection *conn, DBusMessage *msg,
+ void *user_data)
+{
+ struct chrc *chrc = user_data;
+ DBusMessageIter iter;
+ DBusMessage *reply;
+
+ dbus_message_iter_init(msg, &iter);
+
+ if (chrc->notify_io)
+ return g_dbus_create_error(msg,
+ "org.bluez.Error.NotPermitted",
+ NULL);
+
+ if (parse_options(&iter, chrc))
+ return g_dbus_create_error(msg,
+ "org.bluez.Error.InvalidArguments",
+ NULL);
+
+ reply = chrc_create_pipe(chrc, msg);
+
+ if (chrc->notify_io)
+ g_dbus_emit_property_changed(conn, chrc->path, CHRC_INTERFACE,
+ "NotifyAcquired");
+
+ return reply;
+}
+
+static DBusMessage *chrc_start_notify(DBusConnection *conn, DBusMessage *msg,
+ void *user_data)
+{
+ struct chrc *chrc = user_data;
+
+ if (!chrc->notifying)
+ return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+
+ chrc->notifying = true;
+ bt_shell_printf("[" COLORED_CHG "] Attribute %s notifications enabled",
+ chrc->path);
+ g_dbus_emit_property_changed(conn, chrc->path, CHRC_INTERFACE,
+ "Notifying");
+
+ return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *chrc_stop_notify(DBusConnection *conn, DBusMessage *msg,
+ void *user_data)
+{
+ struct chrc *chrc = user_data;
+
+ if (chrc->notifying)
+ return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+
+ chrc->notifying = false;
+ bt_shell_printf("[" COLORED_CHG "] Attribute %s notifications disabled",
+ chrc->path);
+ g_dbus_emit_property_changed(conn, chrc->path, CHRC_INTERFACE,
+ "Notifying");
+
+ return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *chrc_confirm(DBusConnection *conn, DBusMessage *msg,
+ void *user_data)
+{
+ struct chrc *chrc = user_data;
+
+ bt_shell_printf("Attribute %s indication confirm received", chrc->path);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static const GDBusMethodTable chrc_methods[] = {
+ { GDBUS_ASYNC_METHOD("ReadValue", GDBUS_ARGS({ "options", "a{sv}" }),
+ GDBUS_ARGS({ "value", "ay" }),
+ chrc_read_value) },
+ { GDBUS_ASYNC_METHOD("WriteValue", GDBUS_ARGS({ "value", "ay" },
+ { "options", "a{sv}" }),
+ NULL, chrc_write_value) },
+ { GDBUS_METHOD("AcquireWrite", GDBUS_ARGS({ "options", "a{sv}" }),
+ NULL, chrc_acquire_write) },
+ { GDBUS_METHOD("AcquireNotify", GDBUS_ARGS({ "options", "a{sv}" }),
+ NULL, chrc_acquire_notify) },
+ { GDBUS_ASYNC_METHOD("StartNotify", NULL, NULL, chrc_start_notify) },
+ { GDBUS_METHOD("StopNotify", NULL, NULL, chrc_stop_notify) },
+ { GDBUS_METHOD("Confirm", NULL, NULL, chrc_confirm) },
+ { }
+};
+
+static uint8_t *str2bytearray(char *arg, int *val_len)
+{
+ uint8_t value[512];
+ char *entry;
+ unsigned int i;
+
+ for (i = 0; (entry = strsep(&arg, " \t")) != NULL; i++) {
+ long int val;
+ char *endptr = NULL;
+
+ if (*entry == '\0')
+ continue;
+
+ if (i >= G_N_ELEMENTS(value)) {
+ bt_shell_printf("Too much data\n");
+ return NULL;
+ }
+
+ val = strtol(entry, &endptr, 0);
+ if (!endptr || *endptr != '\0' || val > UINT8_MAX) {
+ bt_shell_printf("Invalid value at index %d\n", i);
+ return NULL;
+ }
+
+ value[i] = val;
+ }
+
+ *val_len = i;
+
+ return g_memdup(value, i);
+}
+
+static void chrc_set_value(const char *input, void *user_data)
+{
+ struct chrc *chrc = user_data;
+
+ g_free(chrc->value);
+
+ chrc->value = str2bytearray((char *) input, &chrc->value_len);
+}
+
+void gatt_register_chrc(DBusConnection *conn, GDBusProxy *proxy,
+ int argc, char *argv[])
+{
+ struct service *service;
+ struct chrc *chrc;
+
+ if (!local_services) {
+ bt_shell_printf("No service registered\n");
+ return;
+ }
+
+ service = g_list_last(local_services)->data;
+
+ chrc = g_new0(struct chrc, 1);
+ chrc->service = service;
+ chrc->uuid = g_strdup(argv[1]);
+ chrc->path = g_strdup_printf("%s/chrc%p", service->path, chrc);
+ chrc->flags = g_strsplit(argv[1], ",", -1);
+
+ if (g_dbus_register_interface(conn, chrc->path, CHRC_INTERFACE,
+ chrc_methods, NULL, chrc_properties,
+ chrc, chrc_free) == FALSE) {
+ bt_shell_printf("Failed to register characteristic object\n");
+ chrc_free(chrc);
+ return;
+ }
+
+ service->chrcs = g_list_append(service->chrcs, chrc);
+
+ print_chrc(chrc, COLORED_NEW);
+
+ bt_shell_prompt_input(chrc->path, "Enter value:", chrc_set_value, chrc);
+}
+
+static struct chrc *chrc_find(const char *pattern)
+{
+ GList *l, *lc;
+ struct service *service;
+ struct chrc *chrc;
+
+ for (l = local_services; l; l = g_list_next(l)) {
+ service = l->data;
+
+ for (lc = service->chrcs; lc; lc = g_list_next(lc)) {
+ chrc = lc->data;
+
+ /* match object path */
+ if (!strcmp(chrc->path, pattern))
+ return chrc;
+
+ /* match UUID */
+ if (!strcmp(chrc->uuid, pattern))
+ return chrc;
+ }
+ }
+
+ return NULL;
+}
+
+void gatt_unregister_chrc(DBusConnection *conn, GDBusProxy *proxy,
+ int argc, char *argv[])
+{
+ struct chrc *chrc;
+
+ chrc = chrc_find(argv[1]);
+ if (!chrc) {
+ bt_shell_printf("Failed to unregister characteristic object\n");
+ return;
+ }
+
+ chrc->service->chrcs = g_list_remove(chrc->service->chrcs, chrc);
+
+ chrc_unregister(chrc);
+}
+
+static DBusMessage *desc_read_value(DBusConnection *conn, DBusMessage *msg,
+ void *user_data)
+{
+ struct desc *desc = user_data;
+
+ return read_value(msg, desc->value, desc->value_len);
+}
+
+static DBusMessage *desc_write_value(DBusConnection *conn, DBusMessage *msg,
+ void *user_data)
+{
+ struct desc *desc = user_data;
+ DBusMessageIter iter;
+
+ dbus_message_iter_init(msg, &iter);
+
+ if (parse_value_arg(&iter, &desc->value, &desc->value_len))
+ return g_dbus_create_error(msg,
+ "org.bluez.Error.InvalidArguments",
+ NULL);
+
+ bt_shell_printf("[" COLORED_CHG "] Attribute %s written" , desc->path);
+
+ g_dbus_emit_property_changed(conn, desc->path, CHRC_INTERFACE, "Value");
+
+ return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static const GDBusMethodTable desc_methods[] = {
+ { GDBUS_ASYNC_METHOD("ReadValue", GDBUS_ARGS({ "options", "a{sv}" }),
+ GDBUS_ARGS({ "value", "ay" }),
+ desc_read_value) },
+ { GDBUS_ASYNC_METHOD("WriteValue", GDBUS_ARGS({ "value", "ay" },
+ { "options", "a{sv}" }),
+ NULL, desc_write_value) },
+ { }
+};
+
+static gboolean desc_get_uuid(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct desc *desc = data;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &desc->uuid);
+
+ return TRUE;
+}
+
+static gboolean desc_get_chrc(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct desc *desc = data;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH,
+ &desc->chrc->path);
+
+ return TRUE;
+}
+
+static gboolean desc_get_value(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct desc *desc = data;
+ DBusMessageIter array;
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "y", &array);
+
+ if (desc->value)
+ dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE,
+ &desc->value,
+ desc->value_len);
+
+ dbus_message_iter_close_container(iter, &array);
+
+ return TRUE;
+}
+
+static gboolean desc_get_flags(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct desc *desc = data;
+ int i;
+ DBusMessageIter array;
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "s", &array);
+
+ for (i = 0; desc->flags[i]; i++)
+ dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING,
+ &desc->flags[i]);
+
+ dbus_message_iter_close_container(iter, &array);
+
+ return TRUE;
+}
+
+static const GDBusPropertyTable desc_properties[] = {
+ { "UUID", "s", desc_get_uuid, NULL, NULL },
+ { "Characteristic", "o", desc_get_chrc, NULL, NULL },
+ { "Value", "ay", desc_get_value, NULL, NULL },
+ { "Flags", "as", desc_get_flags, NULL, NULL },
+ { }
+};
+
+static void desc_set_value(const char *input, void *user_data)
+{
+ struct desc *desc = user_data;
+
+ g_free(desc->value);
+
+ desc->value = str2bytearray((char *) input, &desc->value_len);
+}
+
+void gatt_register_desc(DBusConnection *conn, GDBusProxy *proxy,
+ int argc, char *argv[])
+{
+ struct service *service;
+ struct desc *desc;
+
+ if (!local_services) {
+ bt_shell_printf("No service registered\n");
+ return;
+ }
+
+ service = g_list_last(local_services)->data;
+
+ if (!service->chrcs) {
+ bt_shell_printf("No characteristic registered\n");
+ return;
+ }
+
+ desc = g_new0(struct desc, 1);
+ desc->chrc = g_list_last(service->chrcs)->data;
+ desc->uuid = g_strdup(argv[1]);
+ desc->path = g_strdup_printf("%s/desc%p", desc->chrc->path, desc);
+ desc->flags = g_strsplit(argv[1], ",", -1);
+
+ if (g_dbus_register_interface(conn, desc->path, DESC_INTERFACE,
+ desc_methods, NULL, desc_properties,
+ desc, desc_free) == FALSE) {
+ bt_shell_printf("Failed to register descriptor object\n");
+ desc_free(desc);
+ return;
+ }
+
+ desc->chrc->descs = g_list_append(desc->chrc->descs, desc);
+
+ print_desc(desc, COLORED_NEW);
+
+ bt_shell_prompt_input(desc->path, "Enter value:", desc_set_value, desc);
+}
+
+static struct desc *desc_find(const char *pattern)
+{
+ GList *l, *lc, *ld;
+ struct service *service;
+ struct chrc *chrc;
+ struct desc *desc;
+
+ for (l = local_services; l; l = g_list_next(l)) {
+ service = l->data;
+
+ for (lc = service->chrcs; lc; lc = g_list_next(lc)) {
+ chrc = lc->data;
+
+ for (ld = chrc->descs; ld; ld = g_list_next(ld)) {
+ desc = ld->data;
+
+ /* match object path */
+ if (!strcmp(desc->path, pattern))
+ return desc;
+
+ /* match UUID */
+ if (!strcmp(desc->uuid, pattern))
+ return desc;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+void gatt_unregister_desc(DBusConnection *conn, GDBusProxy *proxy,
+ int argc, char *argv[])
+{
+ struct desc *desc;
+
+ desc = desc_find(argv[1]);
+ if (!desc) {
+ bt_shell_printf("Failed to unregister descriptor object\n");
+ return;
+ }
+
+ desc->chrc->descs = g_list_remove(desc->chrc->descs, desc);
+
+ desc_unregister(desc);
+}
void gatt_remove_descriptor(GDBusProxy *proxy);
void gatt_list_attributes(const char *device);
-GDBusProxy *gatt_select_attribute(const char *path);
+GDBusProxy *gatt_select_attribute(GDBusProxy *parent, const char *path);
char *gatt_attribute_generator(const char *text, int state);
void gatt_read_attribute(GDBusProxy *proxy);
void gatt_write_attribute(GDBusProxy *proxy, const char *arg);
void gatt_notify_attribute(GDBusProxy *proxy, bool enable);
+void gatt_acquire_write(GDBusProxy *proxy, const char *arg);
+void gatt_release_write(GDBusProxy *proxy, const char *arg);
+
+void gatt_acquire_notify(GDBusProxy *proxy, const char *arg);
+void gatt_release_notify(GDBusProxy *proxy, const char *arg);
+
void gatt_add_manager(GDBusProxy *proxy);
void gatt_remove_manager(GDBusProxy *proxy);
-void gatt_register_profile(DBusConnection *conn, GDBusProxy *proxy,
- wordexp_t *w);
-void gatt_unregister_profile(DBusConnection *conn, GDBusProxy *proxy);
+void gatt_register_app(DBusConnection *conn, GDBusProxy *proxy,
+ int argc, char *argv[]);
+void gatt_unregister_app(DBusConnection *conn, GDBusProxy *proxy);
+
+void gatt_register_service(DBusConnection *conn, GDBusProxy *proxy,
+ int argc, char *argv[]);
+void gatt_unregister_service(DBusConnection *conn, GDBusProxy *proxy,
+ int argc, char *argv[]);
+
+void gatt_register_chrc(DBusConnection *conn, GDBusProxy *proxy,
+ int argc, char *argv[]);
+void gatt_unregister_chrc(DBusConnection *conn, GDBusProxy *proxy,
+ int argc, char *argv[]);
+
+void gatt_register_desc(DBusConnection *conn, GDBusProxy *proxy,
+ int argc, char *argv[]);
+void gatt_unregister_desc(DBusConnection *conn, GDBusProxy *proxy,
+ int argc, char *argv[]);
#include <unistd.h>
#include <stdlib.h>
#include <stdbool.h>
-#include <signal.h>
-#include <sys/signalfd.h>
#include <wordexp.h>
-#include <readline/readline.h>
-#include <readline/history.h>
#include <glib.h>
+#include "src/shared/shell.h"
#include "src/shared/util.h"
#include "gdbus/gdbus.h"
-#include "monitor/uuid.h"
#include "agent.h"
-#include "display.h"
#include "gatt.h"
#include "advertising.h"
#define PROMPT_ON COLOR_BLUE "[bluetooth]" COLOR_OFF "# "
#define PROMPT_OFF "Waiting to connect to bluetoothd..."
-static GMainLoop *main_loop;
static DBusConnection *dbus_conn;
static GDBusProxy *agent_manager;
struct adapter {
GDBusProxy *proxy;
+ GDBusProxy *ad_proxy;
GList *devices;
};
static struct adapter *default_ctrl;
static GDBusProxy *default_dev;
static GDBusProxy *default_attr;
-static GDBusProxy *ad_manager;
static GList *ctrl_list;
-static guint input = 0;
+static const char *mode_arguments[] = {
+ "on",
+ "off",
+ NULL
+};
-static const char * const agent_arguments[] = {
+static const char *agent_arguments[] = {
"on",
"off",
"DisplayOnly",
NULL
};
-static const char * const ad_arguments[] = {
+static const char *ad_arguments[] = {
"on",
"off",
"peripheral",
printf("Leaking proxy %p\n", data);
}
-static gboolean input_handler(GIOChannel *channel, GIOCondition condition,
- gpointer user_data)
-{
- if (condition & G_IO_IN) {
- rl_callback_read_char();
- return TRUE;
- }
-
- if (condition & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) {
- g_main_loop_quit(main_loop);
- return FALSE;
- }
-
- return TRUE;
-}
-
-static guint setup_standard_input(void)
+static void setup_standard_input(void)
{
- GIOChannel *channel;
- guint source;
-
- channel = g_io_channel_unix_new(fileno(stdin));
-
- source = g_io_add_watch(channel,
- G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
- input_handler, NULL);
-
- g_io_channel_unref(channel);
-
- return source;
+ bt_shell_attach(fileno(stdin));
}
static void connect_handler(DBusConnection *connection, void *user_data)
{
- rl_set_prompt(PROMPT_ON);
- printf("\r");
- rl_on_new_line();
- rl_redisplay();
+ bt_shell_set_prompt(PROMPT_ON);
}
static void disconnect_handler(DBusConnection *connection, void *user_data)
{
- if (input > 0) {
- g_source_remove(input);
- input = 0;
- }
+ bt_shell_detach();
- rl_set_prompt(PROMPT_OFF);
- printf("\r");
- rl_on_new_line();
- rl_redisplay();
+ bt_shell_set_prompt(PROMPT_OFF);
g_list_free_full(ctrl_list, proxy_leak);
ctrl_list = NULL;
else
name = "<unknown>";
- rl_printf("%s%s%sController %s %s %s\n",
+ bt_shell_printf("%s%s%sController %s %s %s\n",
description ? "[" : "",
description ? : "",
description ? "] " : "",
else
name = "<unknown>";
- rl_printf("%s%s%sDevice %s %s\n",
+ bt_shell_printf("%s%s%sDevice %s %s\n",
description ? "[" : "",
description ? : "",
description ? "] " : "",
address, name);
}
+static void print_fixed_iter(const char *label, const char *name,
+ DBusMessageIter *iter)
+{
+ dbus_bool_t *valbool;
+ dbus_uint32_t *valu32;
+ dbus_uint16_t *valu16;
+ dbus_int16_t *vals16;
+ unsigned char *byte;
+ int len;
+
+ switch (dbus_message_iter_get_arg_type(iter)) {
+ case DBUS_TYPE_BOOLEAN:
+ dbus_message_iter_get_fixed_array(iter, &valbool, &len);
+
+ if (len <= 0)
+ return;
+
+ bt_shell_printf("%s%s:\n", label, name);
+ bt_shell_hexdump((void *)valbool, len * sizeof(*valbool));
+
+ break;
+ case DBUS_TYPE_UINT32:
+ dbus_message_iter_get_fixed_array(iter, &valu32, &len);
+
+ if (len <= 0)
+ return;
+
+ bt_shell_printf("%s%s:\n", label, name);
+ bt_shell_hexdump((void *)valu32, len * sizeof(*valu32));
+
+ break;
+ case DBUS_TYPE_UINT16:
+ dbus_message_iter_get_fixed_array(iter, &valu16, &len);
+
+ if (len <= 0)
+ return;
+
+ bt_shell_printf("%s%s:\n", label, name);
+ bt_shell_hexdump((void *)valu16, len * sizeof(*valu16));
+
+ break;
+ case DBUS_TYPE_INT16:
+ dbus_message_iter_get_fixed_array(iter, &vals16, &len);
+
+ if (len <= 0)
+ return;
+
+ bt_shell_printf("%s%s:\n", label, name);
+ bt_shell_hexdump((void *)vals16, len * sizeof(*vals16));
+
+ break;
+ case DBUS_TYPE_BYTE:
+ dbus_message_iter_get_fixed_array(iter, &byte, &len);
+
+ if (len <= 0)
+ return;
+
+ bt_shell_printf("%s%s:\n", label, name);
+ bt_shell_hexdump((void *)byte, len * sizeof(*byte));
+
+ break;
+ default:
+ return;
+ };
+}
+
static void print_iter(const char *label, const char *name,
DBusMessageIter *iter)
{
char *entry;
if (iter == NULL) {
- rl_printf("%s%s is nil\n", label, name);
+ bt_shell_printf("%s%s is nil\n", label, name);
return;
}
switch (dbus_message_iter_get_arg_type(iter)) {
case DBUS_TYPE_INVALID:
- rl_printf("%s%s is invalid\n", label, name);
+ bt_shell_printf("%s%s is invalid\n", label, name);
break;
case DBUS_TYPE_STRING:
case DBUS_TYPE_OBJECT_PATH:
dbus_message_iter_get_basic(iter, &valstr);
- rl_printf("%s%s: %s\n", label, name, valstr);
+ bt_shell_printf("%s%s: %s\n", label, name, valstr);
break;
case DBUS_TYPE_BOOLEAN:
dbus_message_iter_get_basic(iter, &valbool);
- rl_printf("%s%s: %s\n", label, name,
+ bt_shell_printf("%s%s: %s\n", label, name,
valbool == TRUE ? "yes" : "no");
break;
case DBUS_TYPE_UINT32:
dbus_message_iter_get_basic(iter, &valu32);
- rl_printf("%s%s: 0x%06x\n", label, name, valu32);
+ bt_shell_printf("%s%s: 0x%08x\n", label, name, valu32);
break;
case DBUS_TYPE_UINT16:
dbus_message_iter_get_basic(iter, &valu16);
- rl_printf("%s%s: 0x%04x\n", label, name, valu16);
+ bt_shell_printf("%s%s: 0x%04x\n", label, name, valu16);
break;
case DBUS_TYPE_INT16:
dbus_message_iter_get_basic(iter, &vals16);
- rl_printf("%s%s: %d\n", label, name, vals16);
+ bt_shell_printf("%s%s: %d\n", label, name, vals16);
break;
case DBUS_TYPE_BYTE:
dbus_message_iter_get_basic(iter, &byte);
- rl_printf("%s%s: 0x%02x\n", label, name, byte);
+ bt_shell_printf("%s%s: 0x%02x\n", label, name, byte);
break;
case DBUS_TYPE_VARIANT:
dbus_message_iter_recurse(iter, &subiter);
break;
case DBUS_TYPE_ARRAY:
dbus_message_iter_recurse(iter, &subiter);
+
+ if (dbus_type_is_fixed(
+ dbus_message_iter_get_arg_type(&subiter))) {
+ print_fixed_iter(label, name, &subiter);
+ break;
+ }
+
while (dbus_message_iter_get_arg_type(&subiter) !=
DBUS_TYPE_INVALID) {
print_iter(label, name, &subiter);
g_free(entry);
break;
default:
- rl_printf("%s%s has unsupported type\n", label, name);
+ bt_shell_printf("%s%s has unsupported type\n", label, name);
break;
}
}
print_iter("\t", name, &iter);
}
+static void print_uuid(const char *uuid)
+{
+ const char *text;
+
+ text = bt_uuidstr_to_str(uuid);
+ if (text) {
+ char str[26];
+ unsigned int n;
+
+ str[sizeof(str) - 1] = '\0';
+
+ n = snprintf(str, sizeof(str), "%s", text);
+ if (n > sizeof(str) - 1) {
+ str[sizeof(str) - 2] = '.';
+ str[sizeof(str) - 3] = '.';
+ if (str[sizeof(str) - 4] == ' ')
+ str[sizeof(str) - 4] = '.';
+
+ n = sizeof(str) - 1;
+ }
+
+ bt_shell_printf("\tUUID: %s%*c(%s)\n", str, 26 - n, ' ', uuid);
+ } else
+ bt_shell_printf("\tUUID: %*c(%s)\n", 26, ' ', uuid);
+}
+
static void print_uuids(GDBusProxy *proxy)
{
DBusMessageIter iter, value;
dbus_message_iter_recurse(&iter, &value);
while (dbus_message_iter_get_arg_type(&value) == DBUS_TYPE_STRING) {
- const char *uuid, *text;
+ const char *uuid;
dbus_message_iter_get_basic(&value, &uuid);
- text = uuidstr_to_str(uuid);
- if (text) {
- char str[26];
- unsigned int n;
-
- str[sizeof(str) - 1] = '\0';
-
- n = snprintf(str, sizeof(str), "%s", text);
- if (n > sizeof(str) - 1) {
- str[sizeof(str) - 2] = '.';
- str[sizeof(str) - 3] = '.';
- if (str[sizeof(str) - 4] == ' ')
- str[sizeof(str) - 4] = '.';
-
- n = sizeof(str) - 1;
- }
-
- rl_printf("\tUUID: %s%*c(%s)\n",
- str, 26 - n, ' ', uuid);
- } else
- rl_printf("\tUUID: %*c(%s)\n", 26, ' ', uuid);
+ print_uuid(uuid);
dbus_message_iter_next(&value);
}
attribute ? attribute + strlen(path) : "");
done:
- rl_set_prompt(desc ? desc : PROMPT_ON);
- printf("\r");
- rl_on_new_line();
+ bt_shell_set_prompt(desc ? desc : PROMPT_ON);
g_free(desc);
}
}
}
-static void adapter_added(GDBusProxy *proxy)
+static struct adapter *find_ctrl(GList *source, const char *path);
+
+static struct adapter *adapter_new(GDBusProxy *proxy)
{
struct adapter *adapter = g_malloc0(sizeof(struct adapter));
- adapter->proxy = proxy;
ctrl_list = g_list_append(ctrl_list, adapter);
if (!default_ctrl)
default_ctrl = adapter;
+ return adapter;
+}
+
+static void adapter_added(GDBusProxy *proxy)
+{
+ struct adapter *adapter;
+ adapter = find_ctrl(ctrl_list, g_dbus_proxy_get_path(proxy));
+ if (!adapter)
+ adapter = adapter_new(proxy);
+
+ adapter->proxy = proxy;
+
print_adapter(proxy, COLORED_NEW);
}
+static void ad_manager_added(GDBusProxy *proxy)
+{
+ struct adapter *adapter;
+ adapter = find_ctrl(ctrl_list, g_dbus_proxy_get_path(proxy));
+ if (!adapter)
+ adapter = adapter_new(proxy);
+
+ adapter->ad_proxy = proxy;
+}
+
static void proxy_added(GDBusProxy *proxy, void *user_data)
{
const char *interface;
} else if (!strcmp(interface, "org.bluez.GattManager1")) {
gatt_add_manager(proxy);
} else if (!strcmp(interface, "org.bluez.LEAdvertisingManager1")) {
- ad_manager = proxy;
+ ad_manager_added(proxy);
}
}
} else if (!strcmp(interface, "org.bluez.GattManager1")) {
gatt_remove_manager(proxy);
} else if (!strcmp(interface, "org.bluez.LEAdvertisingManager1")) {
- if (ad_manager == proxy) {
- agent_manager = NULL;
- ad_unregister(dbus_conn, NULL);
- }
+ ad_unregister(dbus_conn, NULL);
+ }
+}
+
+static struct adapter *find_ctrl(GList *source, const char *path)
+{
+ GList *list;
+
+ for (list = g_list_first(source); list; list = g_list_next(list)) {
+ struct adapter *adapter = list->data;
+
+ if (!strcasecmp(g_dbus_proxy_get_path(adapter->proxy), path))
+ return adapter;
}
+
+ return NULL;
}
static void property_changed(GDBusProxy *proxy, const char *name,
DBusMessageIter *iter, void *user_data)
{
const char *interface;
+ struct adapter *ctrl;
interface = g_dbus_proxy_get_interface(proxy);
print_iter(str, name, iter);
g_free(str);
+ } else if (!strcmp(interface, "org.bluez.LEAdvertisingManager1")) {
+ DBusMessageIter addr_iter;
+ char *str;
+
+ ctrl = find_ctrl(ctrl_list, g_dbus_proxy_get_path(proxy));
+ if (!ctrl)
+ return;
+
+ if (g_dbus_proxy_get_property(ctrl->proxy, "Address",
+ &addr_iter) == TRUE) {
+ const char *address;
+
+ dbus_message_iter_get_basic(&addr_iter, &address);
+ str = g_strdup_printf("[" COLORED_CHG
+ "] Controller %s ",
+ address);
+ } else
+ str = g_strdup("");
+
+ print_iter(str, name, iter);
+ g_free(str);
} else if (proxy == default_attr) {
char *str;
static void message_handler(DBusConnection *connection,
DBusMessage *message, void *user_data)
{
- rl_printf("[SIGNAL] %s.%s\n", dbus_message_get_interface(message),
+ bt_shell_printf("[SIGNAL] %s.%s\n", dbus_message_get_interface(message),
dbus_message_get_member(message));
}
dbus_message_iter_get_basic(&iter, &str);
- if (!strcmp(str, address))
+ if (!strcasecmp(str, address))
return adapter;
}
dbus_message_iter_get_basic(&iter, &str);
- if (!strcmp(str, address))
+ if (!strcasecmp(str, address))
return proxy;
}
static gboolean check_default_ctrl(void)
{
if (!default_ctrl) {
- rl_printf("No default controller available\n");
+ bt_shell_printf("No default controller available\n");
return FALSE;
}
return TRUE;
}
-static gboolean parse_argument_on_off(const char *arg, dbus_bool_t *value)
-{
- if (!arg || !strlen(arg)) {
- rl_printf("Missing on/off argument\n");
- return FALSE;
- }
-
- if (!strcmp(arg, "on") || !strcmp(arg, "yes")) {
- *value = TRUE;
- return TRUE;
- }
-
- if (!strcmp(arg, "off") || !strcmp(arg, "no")) {
- *value = FALSE;
- return TRUE;
- }
-
- rl_printf("Invalid argument %s\n", arg);
- return FALSE;
-}
-
-static gboolean parse_argument_agent(const char *arg, dbus_bool_t *value,
- const char **capability)
+static gboolean parse_argument(int argc, char *argv[], const char **arg_table,
+ const char *msg, dbus_bool_t *value,
+ const char **option)
{
- const char * const *opt;
-
- if (arg == NULL || strlen(arg) == 0) {
- rl_printf("Missing on/off/capability argument\n");
- return FALSE;
- }
+ const char **opt;
- if (strcmp(arg, "on") == 0 || strcmp(arg, "yes") == 0) {
+ if (!strcmp(argv[1], "on") || !strcmp(argv[1], "yes")) {
*value = TRUE;
- *capability = "";
+ if (option)
+ *option = "";
return TRUE;
}
- if (strcmp(arg, "off") == 0 || strcmp(arg, "no") == 0) {
+ if (!strcmp(argv[1], "off") || !strcmp(argv[1], "no")) {
*value = FALSE;
return TRUE;
}
- for (opt = agent_arguments; *opt; opt++) {
- if (strcmp(arg, *opt) == 0) {
+ for (opt = arg_table; opt && *opt; opt++) {
+ if (strcmp(argv[1], *opt) == 0) {
*value = TRUE;
- *capability = *opt;
+ *option = *opt;
return TRUE;
}
}
- rl_printf("Invalid argument %s\n", arg);
+ bt_shell_printf("Invalid argument %s\n", argv[1]);
return FALSE;
}
-static void cmd_list(const char *arg)
+static void cmd_list(int argc, char *argv[])
{
GList *list;
}
}
-static void cmd_show(const char *arg)
+static void cmd_show(int argc, char *argv[])
{
struct adapter *adapter;
GDBusProxy *proxy;
DBusMessageIter iter;
const char *address;
- if (!arg || !strlen(arg)) {
+ if (argc < 2 || !strlen(argv[1])) {
if (check_default_ctrl() == FALSE)
return;
proxy = default_ctrl->proxy;
} else {
- adapter = find_ctrl_by_address(ctrl_list, arg);
+ adapter = find_ctrl_by_address(ctrl_list, argv[1]);
if (!adapter) {
- rl_printf("Controller %s not available\n", arg);
+ bt_shell_printf("Controller %s not available\n",
+ argv[1]);
return;
}
proxy = adapter->proxy;
return;
dbus_message_iter_get_basic(&iter, &address);
- rl_printf("Controller %s\n", address);
+
+ if (g_dbus_proxy_get_property(proxy, "AddressType", &iter) == TRUE) {
+ const char *type;
+
+ dbus_message_iter_get_basic(&iter, &type);
+
+ bt_shell_printf("Controller %s (%s)\n", address, type);
+ } else {
+ bt_shell_printf("Controller %s\n", address);
+ }
print_property(proxy, "Name");
print_property(proxy, "Alias");
#endif
}
-static void cmd_select(const char *arg)
+static void cmd_select(int argc, char *argv[])
{
struct adapter *adapter;
- if (!arg || !strlen(arg)) {
- rl_printf("Missing controller address argument\n");
- return;
- }
-
- adapter = find_ctrl_by_address(ctrl_list, arg);
+ adapter = find_ctrl_by_address(ctrl_list, argv[1]);
if (!adapter) {
- rl_printf("Controller %s not available\n", arg);
+ bt_shell_printf("Controller %s not available\n", argv[1]);
return;
}
print_adapter(adapter->proxy, NULL);
}
-static void cmd_devices(const char *arg)
+static void cmd_devices(int argc, char *argv[])
{
GList *ll;
}
}
-static void cmd_paired_devices(const char *arg)
+static void cmd_paired_devices(int argc, char *argv[])
{
GList *ll;
char *str = user_data;
if (dbus_error_is_set(error))
- rl_printf("Failed to set %s: %s\n", str, error->name);
+ bt_shell_printf("Failed to set %s: %s\n", str, error->name);
else
- rl_printf("Changing %s succeeded\n", str);
+ bt_shell_printf("Changing %s succeeded\n", str);
}
-static void cmd_system_alias(const char *arg)
+static void cmd_system_alias(int argc, char *argv[])
{
char *name;
- if (!arg || !strlen(arg)) {
- rl_printf("Missing name argument\n");
- return;
- }
-
if (check_default_ctrl() == FALSE)
return;
- name = g_strdup(arg);
+ name = g_strdup(argv[1]);
if (g_dbus_proxy_set_property_basic(default_ctrl->proxy, "Alias",
DBUS_TYPE_STRING, &name,
g_free(name);
}
-static void cmd_reset_alias(const char *arg)
+static void cmd_reset_alias(int argc, char *argv[])
{
char *name;
g_free(name);
}
-static void cmd_power(const char *arg)
+static void cmd_power(int argc, char *argv[])
{
dbus_bool_t powered;
char *str;
- if (parse_argument_on_off(arg, &powered) == FALSE)
+ if (!parse_argument(argc, argv, NULL, NULL, &powered, NULL))
return;
if (check_default_ctrl() == FALSE)
g_free(str);
}
-static void cmd_pairable(const char *arg)
+static void cmd_pairable(int argc, char *argv[])
{
dbus_bool_t pairable;
char *str;
- if (parse_argument_on_off(arg, &pairable) == FALSE)
+ if (!parse_argument(argc, argv, NULL, NULL, &pairable, NULL))
return;
if (check_default_ctrl() == FALSE)
g_free(str);
}
-static void cmd_discoverable(const char *arg)
+static void cmd_discoverable(int argc, char *argv[])
{
dbus_bool_t discoverable;
char *str;
- if (parse_argument_on_off(arg, &discoverable) == FALSE)
+ if (!parse_argument(argc, argv, NULL, NULL, &discoverable, NULL))
return;
if (check_default_ctrl() == FALSE)
g_free(str);
}
-static void cmd_agent(const char *arg)
+static void cmd_agent(int argc, char *argv[])
{
dbus_bool_t enable;
const char *capability;
- if (parse_argument_agent(arg, &enable, &capability) == FALSE)
+ if (!parse_argument(argc, argv, agent_arguments, "capability",
+ &enable, &capability))
return;
if (enable == TRUE) {
agent_register(dbus_conn, agent_manager,
auto_register_agent);
else
- rl_printf("Agent registration enabled\n");
+ bt_shell_printf("Agent registration enabled\n");
} else {
g_free(auto_register_agent);
auto_register_agent = NULL;
if (agent_manager)
agent_unregister(dbus_conn, agent_manager);
else
- rl_printf("Agent registration disabled\n");
+ bt_shell_printf("Agent registration disabled\n");
}
}
-static void cmd_default_agent(const char *arg)
+static void cmd_default_agent(int argc, char *argv[])
{
agent_default(dbus_conn, agent_manager);
}
dbus_error_init(&error);
if (dbus_set_error_from_message(&error, message) == TRUE) {
- rl_printf("Failed to %s discovery: %s\n",
+ bt_shell_printf("Failed to %s discovery: %s\n",
enable == TRUE ? "start" : "stop", error.name);
dbus_error_free(&error);
return;
}
- rl_printf("Discovery %s\n", enable == TRUE ? "started" : "stopped");
-}
-
-static void cmd_scan(const char *arg)
-{
- dbus_bool_t enable;
- const char *method;
-
- if (parse_argument_on_off(arg, &enable) == FALSE)
- return;
-
- if (check_default_ctrl() == FALSE)
- return;
-
- if (enable == TRUE)
- method = "StartDiscovery";
- else
- method = "StopDiscovery";
-
- if (g_dbus_proxy_method_call(default_ctrl->proxy, method,
- NULL, start_discovery_reply,
- GUINT_TO_POINTER(enable), NULL) == FALSE) {
- rl_printf("Failed to %s discovery\n",
- enable == TRUE ? "start" : "stop");
- return;
- }
+ bt_shell_printf("Discovery %s\n", enable == TRUE ? "started" : "stopped");
}
static void append_variant(DBusMessageIter *iter, int type, void *val)
#define DISTANCE_VAL_INVALID 0x7FFF
-struct set_discovery_filter_args {
+static struct set_discovery_filter_args {
char *transport;
dbus_uint16_t rssi;
dbus_int16_t pathloss;
char **uuids;
size_t uuids_len;
+ dbus_bool_t duplicate;
+ bool set;
+} filter = {
+ .rssi = DISTANCE_VAL_INVALID,
+ .pathloss = DISTANCE_VAL_INVALID,
+ .set = true,
};
static void set_discovery_filter_setup(DBusMessageIter *iter, void *user_data)
dict_append_entry(&dict, "Transport", DBUS_TYPE_STRING,
&args->transport);
+ if (args->duplicate)
+ dict_append_entry(&dict, "DuplicateData", DBUS_TYPE_BOOLEAN,
+ &args->duplicate);
+
dbus_message_iter_close_container(iter, &dict);
}
dbus_error_init(&error);
if (dbus_set_error_from_message(&error, message) == TRUE) {
- rl_printf("SetDiscoveryFilter failed: %s\n", error.name);
+ bt_shell_printf("SetDiscoveryFilter failed: %s\n", error.name);
dbus_error_free(&error);
return;
}
- rl_printf("SetDiscoveryFilter success\n");
+ filter.set = true;
+
+ bt_shell_printf("SetDiscoveryFilter success\n");
}
-static gint filtered_scan_rssi = DISTANCE_VAL_INVALID;
-static gint filtered_scan_pathloss = DISTANCE_VAL_INVALID;
-static char **filtered_scan_uuids;
-static size_t filtered_scan_uuids_len;
-static char *filtered_scan_transport;
+static void set_discovery_filter(void)
+{
+ if (check_default_ctrl() == FALSE || filter.set)
+ return;
+
+ if (g_dbus_proxy_method_call(default_ctrl->proxy, "SetDiscoveryFilter",
+ set_discovery_filter_setup, set_discovery_filter_reply,
+ &filter, NULL) == FALSE) {
+ bt_shell_printf("Failed to set discovery filter\n");
+ return;
+ }
+
+ filter.set = true;
+}
-static void cmd_set_scan_filter_commit(void)
+static void cmd_scan(int argc, char *argv[])
{
- struct set_discovery_filter_args args;
+ dbus_bool_t enable;
+ const char *method;
- args.uuids = NULL;
- args.pathloss = filtered_scan_pathloss;
- args.rssi = filtered_scan_rssi;
- args.transport = filtered_scan_transport;
- args.uuids = filtered_scan_uuids;
- args.uuids_len = filtered_scan_uuids_len;
+ if (!parse_argument(argc, argv, NULL, NULL, &enable, NULL))
+ return;
if (check_default_ctrl() == FALSE)
return;
- if (g_dbus_proxy_method_call(default_ctrl->proxy, "SetDiscoveryFilter",
- set_discovery_filter_setup, set_discovery_filter_reply,
- &args, NULL) == FALSE) {
- rl_printf("Failed to set discovery filter\n");
+ if (enable == TRUE) {
+ set_discovery_filter();
+ method = "StartDiscovery";
+ } else
+ method = "StopDiscovery";
+
+ if (g_dbus_proxy_method_call(default_ctrl->proxy, method,
+ NULL, start_discovery_reply,
+ GUINT_TO_POINTER(enable), NULL) == FALSE) {
+ bt_shell_printf("Failed to %s discovery\n",
+ enable == TRUE ? "start" : "stop");
return;
}
}
-static void cmd_set_scan_filter_uuids(const char *arg)
+static void cmd_scan_filter_uuids(int argc, char *argv[])
{
- g_strfreev(filtered_scan_uuids);
- filtered_scan_uuids = NULL;
- filtered_scan_uuids_len = 0;
+ if (argc < 2 || !strlen(argv[1])) {
+ char **uuid;
+
+ for (uuid = filter.uuids; uuid && *uuid; uuid++)
+ print_uuid(*uuid);
+
+ return;
+ }
+
+ g_strfreev(filter.uuids);
+ filter.uuids = NULL;
+ filter.uuids_len = 0;
- if (!arg || !strlen(arg))
+ if (!strcmp(argv[1], "all"))
goto commit;
- filtered_scan_uuids = g_strsplit(arg, " ", -1);
- if (!filtered_scan_uuids) {
- rl_printf("Failed to parse input\n");
+ filter.uuids = g_strdupv(&argv[1]);
+ if (!filter.uuids) {
+ bt_shell_printf("Failed to parse input\n");
return;
}
- filtered_scan_uuids_len = g_strv_length(filtered_scan_uuids);
+ filter.uuids_len = g_strv_length(filter.uuids);
commit:
- cmd_set_scan_filter_commit();
+ filter.set = false;
}
-static void cmd_set_scan_filter_rssi(const char *arg)
+static void cmd_scan_filter_rssi(int argc, char *argv[])
{
- filtered_scan_pathloss = DISTANCE_VAL_INVALID;
+ if (argc < 2 || !strlen(argv[1])) {
+ if (filter.rssi != DISTANCE_VAL_INVALID)
+ bt_shell_printf("RSSI: %d\n", filter.rssi);
+ return;
+ }
- if (!arg || !strlen(arg))
- filtered_scan_rssi = DISTANCE_VAL_INVALID;
- else
- filtered_scan_rssi = atoi(arg);
+ filter.pathloss = DISTANCE_VAL_INVALID;
+ filter.rssi = atoi(argv[1]);
- cmd_set_scan_filter_commit();
+ filter.set = false;
}
-static void cmd_set_scan_filter_pathloss(const char *arg)
+static void cmd_scan_filter_pathloss(int argc, char *argv[])
{
- filtered_scan_rssi = DISTANCE_VAL_INVALID;
+ if (argc < 2 || !strlen(argv[1])) {
+ if (filter.pathloss != DISTANCE_VAL_INVALID)
+ bt_shell_printf("Pathloss: %d\n",
+ filter.pathloss);
+ return;
+ }
- if (!arg || !strlen(arg))
- filtered_scan_pathloss = DISTANCE_VAL_INVALID;
- else
- filtered_scan_pathloss = atoi(arg);
+ filter.rssi = DISTANCE_VAL_INVALID;
+ filter.pathloss = atoi(argv[1]);
- cmd_set_scan_filter_commit();
+ filter.set = false;
}
-static void cmd_set_scan_filter_transport(const char *arg)
+static void cmd_scan_filter_transport(int argc, char *argv[])
{
- g_free(filtered_scan_transport);
+ if (argc < 2 || !strlen(argv[1])) {
+ if (filter.transport)
+ bt_shell_printf("Transport: %s\n",
+ filter.transport);
+ return;
+ }
- if (!arg || !strlen(arg))
- filtered_scan_transport = NULL;
- else
- filtered_scan_transport = g_strdup(arg);
+ g_free(filter.transport);
+ filter.transport = g_strdup(argv[1]);
- cmd_set_scan_filter_commit();
+ filter.set = false;
}
-static void clear_discovery_filter_setup(DBusMessageIter *iter, void *user_data)
+static void cmd_scan_filter_duplicate_data(int argc, char *argv[])
{
- DBusMessageIter dict;
+ if (argc < 2 || !strlen(argv[1])) {
+ bt_shell_printf("DuplicateData: %s\n",
+ filter.duplicate ? "on" : "off");
+ return;
+ }
- dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
- DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
- DBUS_TYPE_STRING_AS_STRING
- DBUS_TYPE_VARIANT_AS_STRING
- DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+ if (!strcmp(argv[1], "on"))
+ filter.duplicate = true;
+ else if (!strcmp(argv[1], "off"))
+ filter.duplicate = false;
+ else {
+ bt_shell_printf("Invalid option: %s\n", argv[1]);
+ return;
+ }
- dbus_message_iter_close_container(iter, &dict);
+ filter.set = false;
}
-static void cmd_set_scan_filter_clear(const char *arg)
+static void filter_clear_uuids(void)
{
- /* set default values for all options */
- filtered_scan_rssi = DISTANCE_VAL_INVALID;
- filtered_scan_pathloss = DISTANCE_VAL_INVALID;
- g_strfreev(filtered_scan_uuids);
- filtered_scan_uuids = NULL;
- filtered_scan_uuids_len = 0;
- g_free(filtered_scan_transport);
- filtered_scan_transport = NULL;
+ g_strfreev(filter.uuids);
+ filter.uuids = NULL;
+ filter.uuids_len = 0;
+}
- if (check_default_ctrl() == FALSE)
- return;
+static void filter_clear_rssi(void)
+{
+ filter.rssi = DISTANCE_VAL_INVALID;
+}
- if (g_dbus_proxy_method_call(default_ctrl->proxy, "SetDiscoveryFilter",
- clear_discovery_filter_setup, set_discovery_filter_reply,
- NULL, NULL) == FALSE) {
- rl_printf("Failed to clear discovery filter\n");
- }
+static void filter_clear_pathloss(void)
+{
+ filter.pathloss = DISTANCE_VAL_INVALID;
}
-static struct GDBusProxy *find_device(const char *arg)
+static void filter_clear_transport(void)
{
- GDBusProxy *proxy;
+ g_free(filter.transport);
+ filter.transport = NULL;
+}
- if (!arg || !strlen(arg)) {
- if (default_dev)
- return default_dev;
- rl_printf("Missing device address argument\n");
- return NULL;
+static void filter_clear_duplicate(void)
+{
+ filter.duplicate = false;
+}
+
+static const struct filter_clear {
+ const char *name;
+ void (*clear) (void);
+} filter_clear[] = {
+ { "uuids", filter_clear_uuids },
+ { "rssi", filter_clear_rssi },
+ { "pathloss", filter_clear_pathloss },
+ { "transport", filter_clear_transport },
+ { "duplicate-data", filter_clear_duplicate },
+ {}
+};
+
+static char *filter_clear_generator(const char *text, int state)
+{
+ static int index, len;
+ const char *arg;
+
+ if (!state) {
+ index = 0;
+ len = strlen(text);
}
- if (check_default_ctrl() == FALSE)
- return NULL;
+ while ((arg = filter_clear[index].name)) {
+ index++;
- proxy = find_proxy_by_address(default_ctrl->devices, arg);
- if (!proxy) {
- rl_printf("Device %s not available\n", arg);
- return NULL;
+ if (!strncmp(arg, text, len))
+ return strdup(arg);
}
- return proxy;
+ return NULL;
}
-static void cmd_info(const char *arg)
+static void cmd_scan_filter_clear(int argc, char *argv[])
+{
+ const struct filter_clear *fc;
+ bool all = false;
+
+ if (argc < 2 || !strlen(argv[1]))
+ all = true;
+
+ for (fc = filter_clear; fc && fc->name; fc++) {
+ if (all || !strcmp(fc->name, argv[1])) {
+ fc->clear();
+ filter.set = false;
+ if (!all)
+ goto done;
+ }
+ }
+
+ if (!all) {
+ bt_shell_printf("Invalid argument %s\n", argv[1]);
+ return;
+ }
+
+done:
+ if (check_default_ctrl() == FALSE)
+ return;
+
+ set_discovery_filter();
+}
+
+static struct GDBusProxy *find_device(int argc, char *argv[])
+{
+ GDBusProxy *proxy;
+
+ if (argc < 2 || !strlen(argv[1])) {
+ if (default_dev)
+ return default_dev;
+ bt_shell_printf("Missing device address argument\n");
+ return NULL;
+ }
+
+ if (check_default_ctrl() == FALSE)
+ return NULL;
+
+ proxy = find_proxy_by_address(default_ctrl->devices, argv[1]);
+ if (!proxy) {
+ bt_shell_printf("Device %s not available\n", argv[1]);
+ return NULL;
+ }
+
+ return proxy;
+}
+
+static void cmd_info(int argc, char *argv[])
{
GDBusProxy *proxy;
DBusMessageIter iter;
const char *address;
- proxy = find_device(arg);
+ proxy = find_device(argc, argv);
if (!proxy)
return;
return;
dbus_message_iter_get_basic(&iter, &address);
- rl_printf("Device %s\n", address);
+
+ if (g_dbus_proxy_get_property(proxy, "AddressType", &iter) == TRUE) {
+ const char *type;
+
+ dbus_message_iter_get_basic(&iter, &type);
+
+ bt_shell_printf("Device %s (%s)\n", address, type);
+ } else {
+ bt_shell_printf("Device %s\n", address);
+ }
print_property(proxy, "Name");
print_property(proxy, "Alias");
dbus_error_init(&error);
if (dbus_set_error_from_message(&error, message) == TRUE) {
- rl_printf("Failed to pair: %s\n", error.name);
+ bt_shell_printf("Failed to pair: %s\n", error.name);
dbus_error_free(&error);
return;
}
- rl_printf("Pairing successful\n");
+ bt_shell_printf("Pairing successful\n");
}
-static void cmd_pair(const char *arg)
+static void cmd_pair(int argc, char *argv[])
{
GDBusProxy *proxy;
- proxy = find_device(arg);
+ proxy = find_device(argc, argv);
if (!proxy)
return;
if (g_dbus_proxy_method_call(proxy, "Pair", NULL, pair_reply,
NULL, NULL) == FALSE) {
- rl_printf("Failed to pair\n");
+ bt_shell_printf("Failed to pair\n");
return;
}
- rl_printf("Attempting to pair with %s\n", arg);
+ bt_shell_printf("Attempting to pair with %s\n", argv[1]);
}
-static void cmd_trust(const char *arg)
+static void cmd_trust(int argc, char *argv[])
{
GDBusProxy *proxy;
dbus_bool_t trusted;
char *str;
- proxy = find_device(arg);
+ proxy = find_device(argc, argv);
if (!proxy)
return;
trusted = TRUE;
- str = g_strdup_printf("%s trust", arg);
+ str = g_strdup_printf("%s trust", argv[1]);
if (g_dbus_proxy_set_property_basic(proxy, "Trusted",
DBUS_TYPE_BOOLEAN, &trusted,
g_free(str);
}
-static void cmd_untrust(const char *arg)
+static void cmd_untrust(int argc, char *argv[])
{
GDBusProxy *proxy;
dbus_bool_t trusted;
char *str;
- proxy = find_device(arg);
+ proxy = find_device(argc, argv);
if (!proxy)
return;
trusted = FALSE;
- str = g_strdup_printf("%s untrust", arg);
+ str = g_strdup_printf("%s untrust", argv[1]);
if (g_dbus_proxy_set_property_basic(proxy, "Trusted",
DBUS_TYPE_BOOLEAN, &trusted,
g_free(str);
}
-static void cmd_block(const char *arg)
+static void cmd_block(int argc, char *argv[])
{
GDBusProxy *proxy;
dbus_bool_t blocked;
char *str;
- proxy = find_device(arg);
+ proxy = find_device(argc, argv);
if (!proxy)
return;
blocked = TRUE;
- str = g_strdup_printf("%s block", arg);
+ str = g_strdup_printf("%s block", argv[1]);
if (g_dbus_proxy_set_property_basic(proxy, "Blocked",
DBUS_TYPE_BOOLEAN, &blocked,
g_free(str);
}
-static void cmd_unblock(const char *arg)
+static void cmd_unblock(int argc, char *argv[])
{
GDBusProxy *proxy;
dbus_bool_t blocked;
char *str;
- proxy = find_device(arg);
+ proxy = find_device(argc, argv);
if (!proxy)
return;
blocked = FALSE;
- str = g_strdup_printf("%s unblock", arg);
+ str = g_strdup_printf("%s unblock", argv[1]);
if (g_dbus_proxy_set_property_basic(proxy, "Blocked",
DBUS_TYPE_BOOLEAN, &blocked,
dbus_error_init(&error);
if (dbus_set_error_from_message(&error, message) == TRUE) {
- rl_printf("Failed to remove device: %s\n", error.name);
+ bt_shell_printf("Failed to remove device: %s\n", error.name);
dbus_error_free(&error);
return;
}
- rl_printf("Device has been removed\n");
+ bt_shell_printf("Device has been removed\n");
}
static void remove_device_setup(DBusMessageIter *iter, void *user_data)
remove_device_setup,
remove_device_reply,
path, g_free) == FALSE) {
- rl_printf("Failed to remove device\n");
+ bt_shell_printf("Failed to remove device\n");
g_free(path);
}
}
-static void cmd_remove(const char *arg)
+static void cmd_remove(int argc, char *argv[])
{
GDBusProxy *proxy;
- if (!arg || !strlen(arg)) {
- rl_printf("Missing device address argument\n");
- return;
- }
-
if (check_default_ctrl() == FALSE)
return;
- if (strcmp(arg, "*") == 0) {
+ if (strcmp(argv[1], "*") == 0) {
GList *list;
for (list = default_ctrl->devices; list;
return;
}
- proxy = find_proxy_by_address(default_ctrl->devices, arg);
+ proxy = find_proxy_by_address(default_ctrl->devices, argv[1]);
if (!proxy) {
- rl_printf("Device %s not available\n", arg);
+ bt_shell_printf("Device %s not available\n", argv[1]);
return;
}
dbus_error_init(&error);
if (dbus_set_error_from_message(&error, message) == TRUE) {
- rl_printf("Failed to connect: %s\n", error.name);
+ bt_shell_printf("Failed to connect: %s\n", error.name);
dbus_error_free(&error);
return;
}
- rl_printf("Connection successful\n");
+ bt_shell_printf("Connection successful\n");
set_default_device(proxy, NULL);
}
-static void cmd_connect(const char *arg)
+static void cmd_connect(int argc, char *argv[])
{
GDBusProxy *proxy;
- if (!arg || !strlen(arg)) {
- rl_printf("Missing device address argument\n");
- return;
- }
-
if (check_default_ctrl() == FALSE)
return;
- proxy = find_proxy_by_address(default_ctrl->devices, arg);
+ proxy = find_proxy_by_address(default_ctrl->devices, argv[1]);
if (!proxy) {
- rl_printf("Device %s not available\n", arg);
+ bt_shell_printf("Device %s not available\n", argv[1]);
return;
}
if (g_dbus_proxy_method_call(proxy, "Connect", NULL, connect_reply,
proxy, NULL) == FALSE) {
- rl_printf("Failed to connect\n");
+ bt_shell_printf("Failed to connect\n");
return;
}
- rl_printf("Attempting to connect to %s\n", arg);
+ bt_shell_printf("Attempting to connect to %s\n", argv[1]);
}
static void disconn_reply(DBusMessage *message, void *user_data)
dbus_error_init(&error);
if (dbus_set_error_from_message(&error, message) == TRUE) {
- rl_printf("Failed to disconnect: %s\n", error.name);
+ bt_shell_printf("Failed to disconnect: %s\n", error.name);
dbus_error_free(&error);
return;
}
- rl_printf("Successful disconnected\n");
+ bt_shell_printf("Successful disconnected\n");
if (proxy != default_dev)
return;
set_default_device(NULL, NULL);
}
-static void cmd_disconn(const char *arg)
+static void cmd_disconn(int argc, char *argv[])
{
GDBusProxy *proxy;
- proxy = find_device(arg);
+ proxy = find_device(argc, argv);
if (!proxy)
return;
if (g_dbus_proxy_method_call(proxy, "Disconnect", NULL, disconn_reply,
proxy, NULL) == FALSE) {
- rl_printf("Failed to disconnect\n");
+ bt_shell_printf("Failed to disconnect\n");
return;
}
- if (strlen(arg) == 0) {
+
+ if (argc < 2 || strlen(argv[1]) == 0) {
DBusMessageIter iter;
+ const char *addr;
if (g_dbus_proxy_get_property(proxy, "Address", &iter) == TRUE)
- dbus_message_iter_get_basic(&iter, &arg);
- }
- rl_printf("Attempting to disconnect from %s\n", arg);
+ dbus_message_iter_get_basic(&iter, &addr);
+
+ bt_shell_printf("Attempting to disconnect from %s\n", addr);
+ } else
+ bt_shell_printf("Attempting to disconnect from %s\n", argv[1]);
}
-static void cmd_list_attributes(const char *arg)
+static void cmd_list_attributes(int argc, char *argv[])
{
GDBusProxy *proxy;
- proxy = find_device(arg);
+ proxy = find_device(argc, argv);
if (!proxy)
return;
gatt_list_attributes(g_dbus_proxy_get_path(proxy));
}
-static void cmd_set_alias(const char *arg)
+static void cmd_set_alias(int argc, char *argv[])
{
char *name;
- if (!arg || !strlen(arg)) {
- rl_printf("Missing name argument\n");
- return;
- }
-
if (!default_dev) {
- rl_printf("No device connected\n");
+ bt_shell_printf("No device connected\n");
return;
}
- name = g_strdup(arg);
+ name = g_strdup(argv[1]);
if (g_dbus_proxy_set_property_basic(default_dev, "Alias",
DBUS_TYPE_STRING, &name,
g_free(name);
}
-static void cmd_select_attribute(const char *arg)
+static void cmd_select_attribute(int argc, char *argv[])
{
GDBusProxy *proxy;
- if (!arg || !strlen(arg)) {
- rl_printf("Missing attribute argument\n");
- return;
- }
-
if (!default_dev) {
- rl_printf("No device connected\n");
+ bt_shell_printf("No device connected\n");
return;
}
- proxy = gatt_select_attribute(arg);
+ proxy = gatt_select_attribute(default_attr, argv[1]);
if (proxy)
set_default_attribute(proxy);
}
-static struct GDBusProxy *find_attribute(const char *arg)
+static struct GDBusProxy *find_attribute(int argc, char *argv[])
{
GDBusProxy *proxy;
- if (!arg || !strlen(arg)) {
+ if (argc < 2 || !strlen(argv[1])) {
if (default_attr)
return default_attr;
- rl_printf("Missing attribute argument\n");
+ bt_shell_printf("Missing attribute argument\n");
return NULL;
}
- proxy = gatt_select_attribute(arg);
+ proxy = gatt_select_attribute(default_attr, argv[1]);
if (!proxy) {
- rl_printf("Attribute %s not available\n", arg);
+ bt_shell_printf("Attribute %s not available\n", argv[1]);
return NULL;
}
return proxy;
}
-static void cmd_attribute_info(const char *arg)
+static void cmd_attribute_info(int argc, char *argv[])
{
GDBusProxy *proxy;
DBusMessageIter iter;
const char *iface, *uuid, *text;
- proxy = find_attribute(arg);
+ proxy = find_attribute(argc, argv);
if (!proxy)
return;
dbus_message_iter_get_basic(&iter, &uuid);
- text = uuidstr_to_str(uuid);
+ text = bt_uuidstr_to_str(uuid);
if (!text)
text = g_dbus_proxy_get_path(proxy);
iface = g_dbus_proxy_get_interface(proxy);
if (!strcmp(iface, "org.bluez.GattService1")) {
- rl_printf("Service - %s\n", text);
+ bt_shell_printf("Service - %s\n", text);
print_property(proxy, "UUID");
print_property(proxy, "Primary");
print_property(proxy, "Characteristics");
print_property(proxy, "Includes");
} else if (!strcmp(iface, "org.bluez.GattCharacteristic1")) {
- rl_printf("Characteristic - %s\n", text);
+ bt_shell_printf("Characteristic - %s\n", text);
print_property(proxy, "UUID");
print_property(proxy, "Service");
print_property(proxy, "Flags");
print_property(proxy, "Descriptors");
} else if (!strcmp(iface, "org.bluez.GattDescriptor1")) {
- rl_printf("Descriptor - %s\n", text);
+ bt_shell_printf("Descriptor - %s\n", text);
print_property(proxy, "UUID");
print_property(proxy, "Characteristic");
}
}
-static void cmd_read(const char *arg)
+static void cmd_read(int argc, char *argv[])
{
if (!default_attr) {
- rl_printf("No attribute selected\n");
+ bt_shell_printf("No attribute selected\n");
return;
}
gatt_read_attribute(default_attr);
}
-static void cmd_write(const char *arg)
+static void cmd_write(int argc, char *argv[])
{
- if (!arg || !strlen(arg)) {
- rl_printf("Missing data argument\n");
+ if (!default_attr) {
+ bt_shell_printf("No attribute selected\n");
return;
}
+ gatt_write_attribute(default_attr, argv[1]);
+}
+
+static void cmd_acquire_write(int argc, char *argv[])
+{
+ if (!default_attr) {
+ bt_shell_printf("No attribute selected\n");
+ return;
+ }
+
+ gatt_acquire_write(default_attr, argv[1]);
+}
+
+static void cmd_release_write(int argc, char *argv[])
+{
+ if (!default_attr) {
+ bt_shell_printf("No attribute selected\n");
+ return;
+ }
+
+ gatt_release_write(default_attr, argv[1]);
+}
+
+static void cmd_acquire_notify(int argc, char *argv[])
+{
+ if (!default_attr) {
+ bt_shell_printf("No attribute selected\n");
+ return;
+ }
+
+ gatt_acquire_notify(default_attr, argv[1]);
+}
+
+static void cmd_release_notify(int argc, char *argv[])
+{
if (!default_attr) {
- rl_printf("No attribute selected\n");
+ bt_shell_printf("No attribute selected\n");
return;
}
- gatt_write_attribute(default_attr, arg);
+ gatt_release_notify(default_attr, argv[1]);
}
-static void cmd_notify(const char *arg)
+static void cmd_notify(int argc, char *argv[])
{
dbus_bool_t enable;
- if (parse_argument_on_off(arg, &enable) == FALSE)
+ if (!parse_argument(argc, argv, NULL, NULL, &enable, NULL))
return;
if (!default_attr) {
- rl_printf("No attribute selected\n");
+ bt_shell_printf("No attribute selected\n");
return;
}
gatt_notify_attribute(default_attr, enable ? true : false);
}
-static void cmd_register_profile(const char *arg)
+static void cmd_register_app(int argc, char *argv[])
{
- wordexp_t w;
+ if (check_default_ctrl() == FALSE)
+ return;
+ gatt_register_app(dbus_conn, default_ctrl->proxy, argc, argv);
+}
+
+static void cmd_unregister_app(int argc, char *argv[])
+{
if (check_default_ctrl() == FALSE)
return;
- if (wordexp(arg, &w, WRDE_NOCMD)) {
- rl_printf("Invalid argument\n");
+ gatt_unregister_app(dbus_conn, default_ctrl->proxy);
+}
+
+static void cmd_register_service(int argc, char *argv[])
+{
+ if (check_default_ctrl() == FALSE)
return;
- }
- if (w.we_wordc == 0) {
- rl_printf("Missing argument\n");
+ gatt_register_service(dbus_conn, default_ctrl->proxy, argc, argv);
+}
+
+static void cmd_unregister_service(int argc, char *argv[])
+{
+ if (check_default_ctrl() == FALSE)
return;
- }
- gatt_register_profile(dbus_conn, default_ctrl->proxy, &w);
+ gatt_unregister_service(dbus_conn, default_ctrl->proxy, argc, argv);
+}
+
+static void cmd_register_characteristic(int argc, char *argv[])
+{
+ if (check_default_ctrl() == FALSE)
+ return;
- wordfree(&w);
+ gatt_register_chrc(dbus_conn, default_ctrl->proxy, argc, argv);
}
-static void cmd_unregister_profile(const char *arg)
+static void cmd_unregister_characteristic(int argc, char *argv[])
{
if (check_default_ctrl() == FALSE)
return;
- gatt_unregister_profile(dbus_conn, default_ctrl->proxy);
+ gatt_unregister_chrc(dbus_conn, default_ctrl->proxy, argc, argv);
}
-static void cmd_version(const char *arg)
+static void cmd_register_descriptor(int argc, char *argv[])
{
- rl_printf("Version %s\n", VERSION);
+ if (check_default_ctrl() == FALSE)
+ return;
+
+ gatt_register_desc(dbus_conn, default_ctrl->proxy, argc, argv);
}
-static void cmd_quit(const char *arg)
+static void cmd_unregister_descriptor(int argc, char *argv[])
{
- g_main_loop_quit(main_loop);
+ if (check_default_ctrl() == FALSE)
+ return;
+
+ gatt_unregister_desc(dbus_conn, default_ctrl->proxy, argc, argv);
}
static char *generic_generator(const char *text, int state,
dbus_message_iter_get_basic(&iter, &str);
- if (!strncmp(str, text, len))
+ if (!strncasecmp(str, text, len))
return strdup(str);
- }
+ }
return NULL;
}
dbus_message_iter_get_basic(&iter, &str);
- if (!strncmp(str, text, len))
+ if (!strncasecmp(str, text, len))
return strdup(str);
}
return gatt_attribute_generator(text, state);
}
-static char *capability_generator(const char *text, int state)
+static char *argument_generator(const char *text, int state,
+ const char * const *args_list)
{
static int index, len;
const char *arg;
len = strlen(text);
}
- while ((arg = agent_arguments[index])) {
+ while ((arg = args_list[index])) {
index++;
if (!strncmp(arg, text, len))
return NULL;
}
-static gboolean parse_argument_advertise(const char *arg, dbus_bool_t *value,
- const char **type)
+static char *mode_generator(const char *text, int state)
{
- const char * const *opt;
-
- if (arg == NULL || strlen(arg) == 0) {
- rl_printf("Missing on/off/type argument\n");
- return FALSE;
- }
-
- if (strcmp(arg, "on") == 0 || strcmp(arg, "yes") == 0) {
- *value = TRUE;
- *type = "";
- return TRUE;
- }
-
- if (strcmp(arg, "off") == 0 || strcmp(arg, "no") == 0) {
- *value = FALSE;
- return TRUE;
- }
-
- for (opt = ad_arguments; *opt; opt++) {
- if (strcmp(arg, *opt) == 0) {
- *value = TRUE;
- *type = *opt;
- return TRUE;
- }
- }
+ return argument_generator(text, state, mode_arguments);
+}
- rl_printf("Invalid argument %s\n", arg);
- return FALSE;
+static char *capability_generator(const char *text, int state)
+{
+ return argument_generator(text, state, agent_arguments);
}
-static void cmd_advertise(const char *arg)
+static void cmd_advertise(int argc, char *argv[])
{
dbus_bool_t enable;
const char *type;
- if (parse_argument_advertise(arg, &enable, &type) == FALSE)
+ if (!parse_argument(argc, argv, ad_arguments, "type",
+ &enable, &type))
return;
- if (!ad_manager) {
- rl_printf("LEAdvertisingManager not found\n");
+ if (!default_ctrl || !default_ctrl->ad_proxy) {
+ bt_shell_printf("LEAdvertisingManager not found\n");
return;
}
if (enable == TRUE)
- ad_register(dbus_conn, ad_manager, type);
+ ad_register(dbus_conn, default_ctrl->ad_proxy, type);
else
- ad_unregister(dbus_conn, ad_manager);
+ ad_unregister(dbus_conn, default_ctrl->ad_proxy);
}
static char *ad_generator(const char *text, int state)
{
- static int index, len;
- const char *arg;
-
- if (!state) {
- index = 0;
- len = strlen(text);
- }
-
- while ((arg = ad_arguments[index])) {
- index++;
+ return argument_generator(text, state, ad_arguments);
+}
- if (!strncmp(arg, text, len))
- return strdup(arg);
- }
+static void cmd_set_advertise_uuids(int argc, char *argv[])
+{
+ ad_advertise_uuids(dbus_conn, argc, argv);
+}
- return NULL;
+static void cmd_set_advertise_service(int argc, char *argv[])
+{
+ ad_advertise_service(dbus_conn, argc, argv);
}
-static void cmd_set_advertise_uuids(const char *arg)
+static void cmd_set_advertise_manufacturer(int argc, char *argv[])
{
- ad_advertise_uuids(arg);
+ ad_advertise_manufacturer(dbus_conn, argc, argv);
}
-static void cmd_set_advertise_service(const char *arg)
+static void cmd_set_advertise_tx_power(int argc, char *argv[])
{
- ad_advertise_service(arg);
+ dbus_bool_t powered;
+
+ if (!parse_argument(argc, argv, NULL, NULL, &powered, NULL))
+ return;
+
+ ad_advertise_tx_power(dbus_conn, powered);
}
-static void cmd_set_advertise_manufacturer(const char *arg)
+static void cmd_set_advertise_name(int argc, char *argv[])
{
- ad_advertise_manufacturer(arg);
+ if (strcmp(argv[1], "on") == 0 || strcmp(argv[1], "yes") == 0) {
+ ad_advertise_name(dbus_conn, true);
+ return;
+ }
+
+ if (strcmp(argv[1], "off") == 0 || strcmp(argv[1], "no") == 0) {
+ ad_advertise_name(dbus_conn, false);
+ return;
+ }
+
+ ad_advertise_local_name(dbus_conn, argv[1]);
}
-static void cmd_set_advertise_tx_power(const char *arg)
+static void cmd_set_advertise_appearance(int argc, char *argv[])
{
- if (arg == NULL || strlen(arg) == 0) {
- rl_printf("Missing on/off argument\n");
+ long int value;
+ char *endptr = NULL;
+
+ if (strcmp(argv[1], "on") == 0 || strcmp(argv[1], "yes") == 0) {
+ ad_advertise_appearance(dbus_conn, true);
return;
}
- if (strcmp(arg, "on") == 0 || strcmp(arg, "yes") == 0) {
- ad_advertise_tx_power(TRUE);
+ if (strcmp(argv[1], "off") == 0 || strcmp(argv[1], "no") == 0) {
+ ad_advertise_appearance(dbus_conn, false);
return;
}
- if (strcmp(arg, "off") == 0 || strcmp(arg, "no") == 0) {
- ad_advertise_tx_power(FALSE);
+ value = strtol(argv[1], &endptr, 0);
+ if (!endptr || *endptr != '\0' || value > UINT16_MAX) {
+ bt_shell_printf("Invalid argument\n");
return;
}
- rl_printf("Invalid argument\n");
+ ad_advertise_local_appearance(dbus_conn, value);
}
-static const struct {
- const char *cmd;
- const char *arg;
- void (*func) (const char *arg);
- const char *desc;
- char * (*gen) (const char *text, int state);
- void (*disp) (char **matches, int num_matches, int max_length);
-} cmd_table[] = {
+static void cmd_set_advertise_duration(int argc, char *argv[])
+{
+ long int value;
+ char *endptr = NULL;
+
+ value = strtol(argv[1], &endptr, 0);
+ if (!endptr || *endptr != '\0' || value > UINT16_MAX) {
+ bt_shell_printf("Invalid argument\n");
+ return;
+ }
+
+ ad_advertise_duration(dbus_conn, value);
+}
+
+static void cmd_set_advertise_timeout(int argc, char *argv[])
+{
+ long int value;
+ char *endptr = NULL;
+
+ value = strtol(argv[1], &endptr, 0);
+ if (!endptr || *endptr != '\0' || value > UINT16_MAX) {
+ bt_shell_printf("Invalid argument\n");
+ return;
+ }
+
+ ad_advertise_timeout(dbus_conn, value);
+}
+
+static const struct bt_shell_menu advertise_menu = {
+ .name = "advertise",
+ .desc = "Advertise Options Submenu",
+ .entries = {
+ { "set-uuids", "[uuid1 uuid2 ...]",
+ cmd_set_advertise_uuids, "Set advertise uuids" },
+ { "set-service", "[uuid] [data=xx xx ...]", cmd_set_advertise_service,
+ "Set advertise service data" },
+ { "set-manufacturer", "[id] [data=xx xx ...]",
+ cmd_set_advertise_manufacturer,
+ "Set advertise manufacturer data" },
+ { "set-tx-power", "<on/off>", cmd_set_advertise_tx_power,
+ "Enable/disable TX power to be advertised",
+ mode_generator },
+ { "set-name", "<on/off/name>", cmd_set_advertise_name,
+ "Enable/disable local name to be advertised" },
+ { "set-appearance", "<value>", cmd_set_advertise_appearance,
+ "Set custom appearance to be advertised" },
+ { "set-duration", "<seconds>", cmd_set_advertise_duration,
+ "Set advertise duration" },
+ { "set-timeout", "<seconds>", cmd_set_advertise_timeout,
+ "Set advertise timeout" },
+ { } },
+};
+
+static const struct bt_shell_menu scan_menu = {
+ .name = "scan",
+ .desc = "Scan Options Submenu",
+ .entries = {
+ { "uuids", "[all/uuid1 uuid2 ...]", cmd_scan_filter_uuids,
+ "Set/Get UUIDs filter" },
+ { "rssi", "[rssi]", cmd_scan_filter_rssi,
+ "Set/Get RSSI filter, and clears pathloss" },
+ { "pathloss", "[pathloss]", cmd_scan_filter_pathloss,
+ "Set/Get Pathloss filter, and clears RSSI" },
+ { "transport", "[transport]", cmd_scan_filter_transport,
+ "Set/Get transport filter" },
+ { "duplicate-data", "[on/off]", cmd_scan_filter_duplicate_data,
+ "Set/Get duplicate data filter",
+ mode_generator },
+ { "clear", "[uuids/rssi/pathloss/transport/duplicate-data]",
+ cmd_scan_filter_clear,
+ "Clears discovery filter.",
+ filter_clear_generator },
+ { } },
+};
+
+static const struct bt_shell_menu gatt_menu = {
+ .name = "gatt",
+ .desc = "Generic Attribute Submenu",
+ .entries = {
+ { "list-attributes", "[dev]", cmd_list_attributes, "List attributes",
+ dev_generator },
+ { "select-attribute", "<attribute/UUID>", cmd_select_attribute,
+ "Select attribute", attribute_generator },
+ { "attribute-info", "[attribute/UUID]", cmd_attribute_info,
+ "Select attribute", attribute_generator },
+ { "read", NULL, cmd_read, "Read attribute value" },
+ { "write", "<data=xx xx ...>", cmd_write,
+ "Write attribute value" },
+ { "acquire-write", NULL, cmd_acquire_write,
+ "Acquire Write file descriptor" },
+ { "release-write", NULL, cmd_release_write,
+ "Release Write file descriptor" },
+ { "acquire-notify", NULL, cmd_acquire_notify,
+ "Acquire Notify file descriptor" },
+ { "release-notify", NULL, cmd_release_notify,
+ "Release Notify file descriptor" },
+ { "notify", "<on/off>", cmd_notify, "Notify attribute value",
+ mode_generator },
+ { "register-application", "[UUID ...]", cmd_register_app,
+ "Register profile to connect" },
+ { "unregister-application", NULL, cmd_unregister_app,
+ "Unregister profile" },
+ { "register-service", "<UUID>", cmd_register_service,
+ "Register application service." },
+ { "unregister-service", "<UUID/object>", cmd_unregister_service,
+ "Unregister application service" },
+ { "register-characteristic", "<UUID> <Flags=read,write,notify...>",
+ cmd_register_characteristic,
+ "Register application characteristic" },
+ { "unregister-characteristic", "<UUID/object>",
+ cmd_unregister_characteristic,
+ "Unregister application characteristic" },
+ { "register-descriptor", "<UUID> <Flags=read,write...>",
+ cmd_register_descriptor,
+ "Register application descriptor" },
+ { "unregister-descriptor", "<UUID/object>",
+ cmd_unregister_descriptor,
+ "Unregister application descriptor" },
+ { } },
+};
+
+static const struct bt_shell_menu main_menu = {
+ .name = "main",
+ .entries = {
{ "list", NULL, cmd_list, "List available controllers" },
{ "show", "[ctrl]", cmd_show, "Controller information",
ctrl_generator },
{ "devices", NULL, cmd_devices, "List available devices" },
{ "paired-devices", NULL, cmd_paired_devices,
"List paired devices"},
- { "system-alias", "<name>", cmd_system_alias },
- { "reset-alias", NULL, cmd_reset_alias },
- { "power", "<on/off>", cmd_power, "Set controller power" },
+ { "system-alias", "<name>", cmd_system_alias,
+ "Set controller alias" },
+ { "reset-alias", NULL, cmd_reset_alias,
+ "Reset controller alias" },
+ { "power", "<on/off>", cmd_power, "Set controller power",
+ mode_generator },
{ "pairable", "<on/off>", cmd_pairable,
- "Set controller pairable mode" },
+ "Set controller pairable mode",
+ mode_generator },
{ "discoverable", "<on/off>", cmd_discoverable,
- "Set controller discoverable mode" },
+ "Set controller discoverable mode",
+ mode_generator },
{ "agent", "<on/off/capability>", cmd_agent,
"Enable/disable agent with given capability",
capability_generator},
{ "advertise", "<on/off/type>", cmd_advertise,
"Enable/disable advertising with given type",
ad_generator},
- { "set-advertise-uuids", "[uuid1 uuid2 ...]",
- cmd_set_advertise_uuids, "Set advertise uuids" },
- { "set-advertise-service", "[uuid][data=[xx xx ...]",
- cmd_set_advertise_service,
- "Set advertise service data" },
- { "set-advertise-manufacturer", "[id][data=[xx xx ...]",
- cmd_set_advertise_manufacturer,
- "Set advertise manufacturer data" },
- { "set-advertise-tx-power", "<on/off>",
- cmd_set_advertise_tx_power,
- "Enable/disable TX power to be advertised" },
- { "set-scan-filter-uuids", "[uuid1 uuid2 ...]",
- cmd_set_scan_filter_uuids, "Set scan filter uuids" },
- { "set-scan-filter-rssi", "[rssi]", cmd_set_scan_filter_rssi,
- "Set scan filter rssi, and clears pathloss" },
- { "set-scan-filter-pathloss", "[pathloss]",
- cmd_set_scan_filter_pathloss,
- "Set scan filter pathloss, and clears rssi" },
- { "set-scan-filter-transport", "[transport]",
- cmd_set_scan_filter_transport, "Set scan filter transport" },
- { "set-scan-filter-clear", "", cmd_set_scan_filter_clear,
- "Clears discovery filter." },
- { "scan", "<on/off>", cmd_scan, "Scan for devices" },
+ { "set-alias", "<alias>", cmd_set_alias, "Set device alias" },
+ { "scan", "<on/off>", cmd_scan, "Scan for devices",
+ mode_generator },
{ "info", "[dev]", cmd_info, "Device information",
dev_generator },
{ "pair", "[dev]", cmd_pair, "Pair with device",
dev_generator },
{ "disconnect", "[dev]", cmd_disconn, "Disconnect device",
dev_generator },
- { "list-attributes", "[dev]", cmd_list_attributes, "List attributes",
- dev_generator },
- { "set-alias", "<alias>", cmd_set_alias, "Set device alias" },
- { "select-attribute", "<attribute>", cmd_select_attribute,
- "Select attribute", attribute_generator },
- { "attribute-info", "[attribute]", cmd_attribute_info,
- "Select attribute", attribute_generator },
- { "read", NULL, cmd_read, "Read attribute value" },
- { "write", "<data=[xx xx ...]>", cmd_write,
- "Write attribute value" },
- { "notify", "<on/off>", cmd_notify, "Notify attribute value" },
- { "register-profile", "<UUID ...>", cmd_register_profile,
- "Register profile to connect" },
- { "unregister-profile", NULL, cmd_unregister_profile,
- "Unregister profile" },
- { "version", NULL, cmd_version, "Display version" },
- { "quit", NULL, cmd_quit, "Quit program" },
- { "exit", NULL, cmd_quit },
- { "help" },
- { }
+ { } },
};
-static char *cmd_generator(const char *text, int state)
-{
- static int index, len;
- const char *cmd;
-
- if (!state) {
- index = 0;
- len = strlen(text);
- }
-
- while ((cmd = cmd_table[index].cmd)) {
- index++;
-
- if (!strncmp(cmd, text, len))
- return strdup(cmd);
- }
-
- return NULL;
-}
-
-static char **cmd_completion(const char *text, int start, int end)
-{
- char **matches = NULL;
-
- if (agent_completion() == TRUE) {
- rl_attempted_completion_over = 1;
- return NULL;
- }
-
- if (start > 0) {
- int i;
-
- for (i = 0; cmd_table[i].cmd; i++) {
- if (strncmp(cmd_table[i].cmd,
- rl_line_buffer, start - 1))
- continue;
-
- if (!cmd_table[i].gen)
- continue;
-
- rl_completion_display_matches_hook = cmd_table[i].disp;
- matches = rl_completion_matches(text, cmd_table[i].gen);
- break;
- }
- } else {
- rl_completion_display_matches_hook = NULL;
- matches = rl_completion_matches(text, cmd_generator);
- }
-
- if (!matches)
- rl_attempted_completion_over = 1;
-
- return matches;
-}
-
-static void rl_handler(char *input)
-{
- char *cmd, *arg;
- int i;
-
- if (!input) {
- rl_insert_text("quit");
- rl_redisplay();
- rl_crlf();
- g_main_loop_quit(main_loop);
- return;
- }
-
- if (!strlen(input))
- goto done;
-
- if (agent_input(dbus_conn, input) == TRUE)
- goto done;
-
- add_history(input);
-
- cmd = strtok_r(input, " ", &arg);
- if (!cmd)
- goto done;
-
- if (arg) {
- int len = strlen(arg);
- if (len > 0 && arg[len - 1] == ' ')
- arg[len - 1] = '\0';
- }
-
- for (i = 0; cmd_table[i].cmd; i++) {
- if (strcmp(cmd, cmd_table[i].cmd))
- continue;
-
- if (cmd_table[i].func) {
- cmd_table[i].func(arg);
- goto done;
- }
- }
-
- if (strcmp(cmd, "help")) {
- printf("Invalid command\n");
- goto done;
- }
-
- printf("Available commands:\n");
-
- for (i = 0; cmd_table[i].cmd; i++) {
- if (cmd_table[i].desc)
- printf(" %s %-*s %s\n", cmd_table[i].cmd,
- (int)(25 - strlen(cmd_table[i].cmd)),
- cmd_table[i].arg ? : "",
- cmd_table[i].desc ? : "");
- }
-
-done:
- free(input);
-}
-
-static gboolean signal_handler(GIOChannel *channel, GIOCondition condition,
- gpointer user_data)
-{
- static bool terminated = false;
- struct signalfd_siginfo si;
- ssize_t result;
- int fd;
-
- if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
- g_main_loop_quit(main_loop);
- return FALSE;
- }
-
- fd = g_io_channel_unix_get_fd(channel);
-
- result = read(fd, &si, sizeof(si));
- if (result != sizeof(si))
- return FALSE;
-
- switch (si.ssi_signo) {
- case SIGINT:
- if (input) {
- rl_replace_line("", 0);
- rl_crlf();
- rl_on_new_line();
- rl_redisplay();
- break;
- }
-
- /*
- * If input was not yet setup up that means signal was received
- * while daemon was not yet running. Since user is not able
- * to terminate client by CTRL-D or typing exit treat this as
- * exit and fall through.
- */
- case SIGTERM:
- if (!terminated) {
- rl_replace_line("", 0);
- rl_crlf();
- g_main_loop_quit(main_loop);
- }
-
- terminated = true;
- break;
- }
-
- return TRUE;
-}
-
-static guint setup_signalfd(void)
-{
- GIOChannel *channel;
- guint source;
- sigset_t mask;
- int fd;
-
- sigemptyset(&mask);
- sigaddset(&mask, SIGINT);
- sigaddset(&mask, SIGTERM);
-
- if (sigprocmask(SIG_BLOCK, &mask, NULL) < 0) {
- perror("Failed to set signal mask");
- return 0;
- }
-
- fd = signalfd(-1, &mask, 0);
- if (fd < 0) {
- perror("Failed to create signal descriptor");
- return 0;
- }
-
- channel = g_io_channel_unix_new(fd);
-
- g_io_channel_set_close_on_unref(channel, TRUE);
- g_io_channel_set_encoding(channel, NULL, NULL);
- g_io_channel_set_buffered(channel, FALSE);
-
- source = g_io_add_watch(channel,
- G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
- signal_handler, NULL);
-
- g_io_channel_unref(channel);
-
- return source;
-}
+static const struct option options[] = {
+ { "agent", required_argument, 0, 'a' },
+ { 0, 0, 0, 0 }
+};
-static gboolean option_version = FALSE;
+static const char *agent_option;
-static gboolean parse_agent(const char *key, const char *value,
- gpointer user_data, GError **error)
-{
- if (value)
- auto_register_agent = g_strdup(value);
- else
- auto_register_agent = g_strdup("");
+static const char **optargs[] = {
+ &agent_option
+};
- return TRUE;
-}
+static const char *help[] = {
+ "Register agent handler: <capability>"
+};
-static GOptionEntry options[] = {
- { "version", 'v', 0, G_OPTION_ARG_NONE, &option_version,
- "Show version information and exit" },
- { "agent", 'a', G_OPTION_FLAG_OPTIONAL_ARG,
- G_OPTION_ARG_CALLBACK, parse_agent,
- "Register agent handler", "CAPABILITY" },
- { NULL },
+static const struct bt_shell_opt opt = {
+ .options = options,
+ .optno = sizeof(options) / sizeof(struct option),
+ .optstr = "a:",
+ .optarg = optargs,
+ .help = help,
};
static void client_ready(GDBusClient *client, void *user_data)
{
- if (!input)
- input = setup_standard_input();
+ setup_standard_input();
}
int main(int argc, char *argv[])
{
- GOptionContext *context;
- GError *error = NULL;
GDBusClient *client;
- guint signal;
- context = g_option_context_new(NULL);
- g_option_context_add_main_entries(context, options, NULL);
+ bt_shell_init(argc, argv, &opt);
+ bt_shell_set_menu(&main_menu);
+ bt_shell_add_submenu(&advertise_menu);
+ bt_shell_add_submenu(&scan_menu);
+ bt_shell_add_submenu(&gatt_menu);
+ bt_shell_set_prompt(PROMPT_OFF);
- if (g_option_context_parse(context, &argc, &argv, &error) == FALSE) {
- if (error != NULL) {
- g_printerr("%s\n", error->message);
- g_error_free(error);
- } else
- g_printerr("An unknown error occurred\n");
- exit(1);
- }
-
- g_option_context_free(context);
-
- if (option_version == TRUE) {
- printf("%s\n", VERSION);
- exit(0);
- }
+ if (agent_option)
+ auto_register_agent = g_strdup(agent_option);
+ else
+ auto_register_agent = g_strdup("");
- main_loop = g_main_loop_new(NULL, FALSE);
dbus_conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, NULL);
+ g_dbus_attach_object_manager(dbus_conn);
- setlinebuf(stdout);
- rl_attempted_completion_function = cmd_completion;
-
- rl_erase_empty_line = 1;
- rl_callback_handler_install(NULL, rl_handler);
-
- rl_set_prompt(PROMPT_OFF);
- rl_redisplay();
-
- signal = setup_signalfd();
client = g_dbus_client_new(dbus_conn, "org.bluez", "/org/bluez");
g_dbus_client_set_connect_watch(client, connect_handler, NULL);
g_dbus_client_set_ready_watch(client, client_ready, NULL);
- g_main_loop_run(main_loop);
+ bt_shell_run();
g_dbus_client_unref(client);
- g_source_remove(signal);
- if (input > 0)
- g_source_remove(input);
-
- rl_message("");
- rl_callback_handler_remove();
dbus_connection_unref(dbus_conn);
- g_main_loop_unref(main_loop);
g_list_free_full(ctrl_list, proxy_leak);
AC_PREREQ(2.60)
-AC_INIT(bluez, 5.43)
+AC_INIT(bluez, 5.48)
AM_INIT_AUTOMAKE([foreign subdir-objects color-tests silent-rules
tar-pax no-dist-gzip dist-xz])
if (test "$USE_MAINTAINER_MODE" = "yes"); then
AC_CHECK_PROG(enable_coverage, [lcov], [yes], [no])
+ AC_CHECK_PROG(enable_dbus_run_session, [dbus-run-session], [yes])
+ AC_CHECK_PROG(enable_valgrind, [valgrind], [yes])
+ AC_CHECK_HEADERS(valgrind/memcheck.h)
fi
AM_CONDITIONAL(COVERAGE, test "${enable_coverage}" = "yes")
+AM_CONDITIONAL(DBUS_RUN_SESSION, test "${enable_dbus_run_session}" = "yes")
+AM_CONDITIONAL(VALGRIND, test "${enable_valgrind}" = "yes")
MISC_FLAGS
[disable CUPS printer support]), [enable_cups=${enableval}])
AM_CONDITIONAL(CUPS, test "${enable_cups}" != "no")
+AC_ARG_ENABLE(mesh, AC_HELP_STRING([--enable-mesh],
+ [enable Mesh profile support]), [enable_mesh=${enableval}])
+AM_CONDITIONAL(MESH, test "${enable_mesh}" = "yes")
+
+if (test "${enable_mesh}" == "yes"); then
+ PKG_CHECK_MODULES(JSONC, json-c, dummy=yes,
+ AC_MSG_ERROR(json-c is required))
+ AC_SUBST(JSON_CFLAGS)
+ AC_SUBST(JSON_LIBS)
+fi
AC_ARG_ENABLE(midi, AC_HELP_STRING([--enable-midi],
[enable MIDI support]), [enable_midi=${enableval}])
-AM_CONDITIONAL(MIDI, test "${enable_midi}" = "no")
+AM_CONDITIONAL(MIDI, test "${enable_midi}" = "yes")
if (test "${enable_midi}" = "yes"); then
- PKG_CHECK_MODULES(ALSA, alsa, dummy=no,
+ PKG_CHECK_MODULES(ALSA, alsa, dummy=yes,
AC_MSG_ERROR(ALSA lib is required for MIDI support))
AC_SUBST(ALSA_CFLAGS)
AC_SUBST(ALSA_LIBS)
fi
+
AC_ARG_ENABLE(obex, AC_HELP_STRING([--disable-obex],
- [disable OBEX profile support]), [enable_obex=${enableval}])
-
+ [disable OBEX profile support]), [enable_obex=${enableval}])
+
AC_SUBST(ICAL_CFLAGS)
AC_SUBST(ICAL_LIBS)
AM_CONDITIONAL(OBEX, test "${enable_obex}" != "no")
+AC_ARG_ENABLE(btpclient, AC_HELP_STRING([--enable-btpclient],
+ [enable BTP client]), [enable_btpclient=${enableval}])
+AM_CONDITIONAL(BTPCLIENT, test "${enable_btpclient}" = "yes")
+
+if (test "${enable_btpclient}" = "yes"); then
+ PKG_CHECK_MODULES(ELL, ell >= 0.2, dummy=yes,
+ AC_MSG_ERROR(ell library >= 0.2 is required))
+ AC_SUBST(ELL_CFLAGS)
+ AC_SUBST(ELL_LIBS)
+fi
+
AC_ARG_ENABLE(client, AC_HELP_STRING([--disable-client],
[disable command line client]), [enable_client=${enableval}])
AM_CONDITIONAL(CLIENT, test "${enable_client}" != "no")
-if (test "${enable_client}" != "no"); then
+if (test "${enable_client}" != "no" || test "${enable_mesh}" == "yes"); then
AC_CHECK_HEADERS(readline/readline.h, enable_readline=yes,
AC_MSG_ERROR(readline header files are required))
fi
[enable_manpages=${enableval}])
AM_CONDITIONAL(MANPAGES, test "${enable_manpages}" = "yes")
+AC_ARG_ENABLE(testing, AC_HELP_STRING([--enable-testing],
+ [enable testing tools]),
+ [enable_testing=${enableval}])
+AM_CONDITIONAL(TESTING, test "${enable_testing}" = "yes")
+
AC_ARG_ENABLE(experimental, AC_HELP_STRING([--enable-experimental],
[enable experimental tools]),
[enable_experimental=${enableval}])
# End of TIZEN_FEATURE_BLUEZ_MODIFY
AC_ARG_ENABLE(deprecated, AC_HELP_STRING([--enable-deprecated],
- [enable deprecated plugins (BLE services, ...)]),
+ [enable deprecated tools]),
[enable_deprecated=${enableval}])
AM_CONDITIONAL(DEPRECATED, test "${enable_deprecated}" = "yes")
Parameters that may be set in the filter dictionary
include the following:
- array{string} UUIDs : filtered service UUIDs
- int16 RSSI : RSSI threshold value
- uint16 Pathloss : Pathloss threshold value
- string Transport : type of scan to run
-
- When a remote device is found that advertises any UUID
- from UUIDs, it will be reported if:
- - Pathloss and RSSI are both empty,
- - only Pathloss param is set, device advertise TX pwer,
- and computed pathloss is less than Pathloss param,
- - only RSSI param is set, and received RSSI is higher
- than RSSI param,
-
- Transport parameter determines the type of scan:
- "auto" - interleaved scan, default value
- "bredr" - BR/EDR inquiry
- "le" - LE scan only
-
- If "le" or "bredr" Transport is requested, and the
- controller doesn't support it, org.bluez.Error.Failed
- error will be returned. If "auto" transport is
- requested, scan will use LE, BREDR, or both, depending
- on what's currently enabled on the controller.
+ array{string} UUIDs
+
+ Filter by service UUIDs, empty means match
+ _any_ UUID.
+
+ When a remote device is found that advertises
+ any UUID from UUIDs, it will be reported if:
+ - Pathloss and RSSI are both empty.
+ - only Pathloss param is set, device advertise
+ TX pwer, and computed pathloss is less than
+ Pathloss param.
+ - only RSSI param is set, and received RSSI is
+ higher than RSSI param.
+
+ int16 RSSI
+
+ RSSI threshold value.
+
+ PropertiesChanged signals will be emitted
+ for already existing Device objects, with
+ updated RSSI value. If one or more discovery
+ filters have been set, the RSSI delta-threshold,
+ that is imposed by StartDiscovery by default,
+ will not be applied.
+
+ uint16 Pathloss
+
+ Pathloss threshold value.
+
+ PropertiesChanged signals will be emitted
+ for already existing Device objects, with
+ updated Pathloss value.
+
+ string Transport (Default "auto")
+
+ Transport parameter determines the type of
+ scan.
+
+ Possible values:
+ "auto" - interleaved scan
+ "bredr" - BR/EDR inquiry
+ "le" - LE scan only
+
+ If "le" or "bredr" Transport is requested,
+ and the controller doesn't support it,
+ org.bluez.Error.Failed error will be returned.
+ If "auto" transport is requested, scan will use
+ LE, BREDR, or both, depending on what's
+ currently enabled on the controller.
+
+ bool DuplicateData (Default: true)
+
+ Disables duplicate detection of advertisement
+ data.
+
+ When enabled PropertiesChanged signals will be
+ generated for either ManufacturerData and
+ ServiceData everytime they are discovered.
When discovery filter is set, Device objects will be
created as new devices with matching criteria are
discoverable which enables listening to
non-connectable and non-discoverable devices.
- PropertiesChanged signals will be emitted
- for already existing Device objects, with updated RSSI
- value. If one or more discovery filters have been set,
- the RSSI delta-threshold, that is imposed by
- StartDiscovery by default, will not be applied.
-
When multiple clients call SetDiscoveryFilter, their
filters are internally merged, and notifications about
new devices are sent to all clients. Therefore, each
org.bluez.Error.NotSupported
org.bluez.Error.Failed
+ array{string} GetDiscoveryFilters()
+
+ Return available filters that can be given to
+ SetDiscoveryFilter.
+
+ Possible errors: None
+
#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
void StartCustomDiscovery(string pattern)
The Bluetooth device address.
+ string AddressType [readonly]
+
+ The Bluetooth Address Type. For dual-mode and BR/EDR
+ only adapter this defaults to "public". Single mode LE
+ adapters may have either value. With privacy enabled
+ this contains type of Identity Address and not type of
+ address used for connection.
+
+ Possible values:
+ "public" - Public address
+ "random" - Random address
+
string Name [readonly]
The Bluetooth system name (pretty hostname).
Service Data elements to include. The keys are the
UUID to associate with the data.
- bool IncludeTxPower
+ array{string} Includes
- Includes the Tx Power in the advertising packet.
- If missing, the Tx Power is not included.
+ List of features to be included in the advertising
+ packet.
+ Possible values: as found on
+ LEAdvertisingManager.SupportedIncludes
+
+ string LocalName
+
+ Local name to be used in the advertising report. If the
+ string is too big to fit into the packet it will be
+ truncated.
+
+ If this property is available 'local-name' cannot be
+ present in the Includes.
+
+ uint16 Appearance
+
+ Appearance to be used in the advertising report.
+
+ Possible values: as found on GAP Service.
+
+ uint16_t Duration
+
+ Duration of the advertisement in seconds. If there are
+ other applications advertising no duration is set the
+ default is 2 seconds.
+
+ uint16_t Timeout
+
+ Timeout of the advertisement in seconds. This defines
+ the lifetime of the advertisement.
LE Advertising Manager hierarchy
================================
follow the API for LE Advertisement Data described above.
Service org.bluez
-Interface org.bluez.LEAdvertisingManager1 [Experimental]
+Interface org.bluez.LEAdvertisingManager1
Object path /org/bluez/{hci0,hci1,...}
Methods RegisterAdvertisement(object advertisement, dict options)
Registers an advertisement object to be sent over the LE
Advertising channel. The service must be exported
- under interface LEAdvertisement1. InvalidArguments
- indicates that the object has invalid or conflicting
- properties. InvalidLength indicates that the data
+ under interface LEAdvertisement1.
+
+ InvalidArguments error indicates that the object has
+ invalid or conflicting properties.
+
+ InvalidLength error indicates that the data
provided generates a data packet which is too long.
- The properties of this object are parser when it is
+
+ The properties of this object are parsed when it is
registered, and any changes are ignored.
- Currently only one advertisement at a time is supported,
- attempting to register two advertisements will result in
+
+ If the same object is registered twice it will result in
an AlreadyExists error.
+ If the maximum number of advertisement instances is
+ reached it will result in NotPermitted error.
+
Possible errors: org.bluez.Error.InvalidArguments
org.bluez.Error.AlreadyExists
org.bluez.Error.InvalidLength
+ org.bluez.Error.NotPermitted
UnregisterAdvertisement(object advertisement)
This unregisters an advertisement that has been
- prevously registered. The object path parameter must
+ previously registered. The object path parameter must
match the same value that has been used on registration.
Possible errors: org.bluez.Error.InvalidArguments
org.bluez.Error.DoesNotExist
+
+Properties byte ActiveInstances
+
+ Number of active advertising instances.
+
+ byte SupportedInstances
+
+ Number of available advertising instances.
+
+ array{string} SupportedIncludes
+
+ List of supported system includes.
+
+ Possible values: "tx-power"
+ "appearance"
+ "local-name"
This method gets called to request the user to
authorize an incoming pairing attempt which
would in other circumstances trigger the just-works
- model.
+ model, or when the user plugged in a device that
+ implements cable pairing. In the latter case, the
+ device would not be connected to the adapter via
+ Bluetooth yet.
Possible errors: org.bluez.Error.Rejected
org.bluez.Error.Canceled
--- /dev/null
+BlueZ D-Bus Battery API description
+***********************************
+
+
+Battery hierarchy
+=================
+
+Service org.bluez
+Interface org.bluez.Battery1
+Object path [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX
+
+Properties byte Percentage [readonly]
+
+ The percentage of battery left as an unsigned 8-bit integer.
This method can be also used to cancel a preceding
Connect call before a reply to it has been received.
+ For non-trusted devices connected over LE bearer calling
+ this method will disable incoming connections until
+ Connect method is called again.
+
Possible errors: org.bluez.Error.NotConnected
void ConnectProfile(string uuid)
The Bluetooth device address of the remote device.
+ string AddressType [readonly]
+
+ The Bluetooth device Address Type. For dual-mode and
+ BR/EDR only devices this defaults to "public". Single
+ mode LE devices may have either value. If remote device
+ uses privacy than before pairing this represents address
+ type used for connection and Identity Address after
+ pairing.
+
+ Possible values:
+ "public" - Public address
+ "random" - Random address
+
string Name [readonly, optional]
The Bluetooth remote name. This value can not be
org.bluez.Error.NotAuthorized
org.bluez.Error.NotSupported
+ fd, uint16 AcquireWrite(dict options) [optional]
+
+ Acquire file descriptor and MTU for writing. Usage of
+ WriteValue will be locked causing it to return
+ NotPermitted error.
+
+ For server the MTU returned shall be equal or smaller
+ than the negotiated MTU.
+
+ For client it only works with characteristic that has
+ WriteAcquired property which relies on
+ write-without-response Flag.
+
+ To release the lock the client shall close the file
+ descriptor, a HUP is generated in case the device
+ is disconnected.
+
+ Note: the MTU can only be negotiated once and is
+ symmetric therefore this method may be delayed in
+ order to have the exchange MTU completed, because of
+ that the file descriptor is closed during
+ reconnections as the MTU has to be renegotiated.
+
+ Possible options: "device": Object Device (Server only)
+ "MTU": Exchanged MTU (Server only)
+
+ Possible Errors: org.bluez.Error.Failed
+ org.bluez.Error.NotSupported
+
+ fd, uint16 AcquireNotify(dict options) [optional]
+
+ Acquire file descriptor and MTU for notify. Usage of
+ StartNotify will be locked causing it to return
+ NotPermitted error.
+
+ For server the MTU returned shall be equal or smaller
+ than the negotiated MTU.
+
+ Only works with characteristic that has NotifyAcquired
+ which relies on notify Flag and no other client have
+ called StartNotify.
+
+ Notification are enabled during this procedure so
+ StartNotify shall not be called, any notification
+ will be dispatched via file descriptor therefore the
+ Value property is not affected during the time where
+ notify has been acquired.
+
+ To release the lock the client shall close the file
+ descriptor, a HUP is generated in case the device
+ is disconnected.
+
+ Note: the MTU can only be negotiated once and is
+ symmetric therefore this method may be delayed in
+ order to have the exchange MTU completed, because of
+ that the file descriptor is closed during
+ reconnections as the MTU has to be renegotiated.
+
+ Possible options: "device": Object Device (Server only)
+ "MTU": Exchanged MTU (Server only)
+
+ Possible Errors: org.bluez.Error.Failed
+ org.bluez.Error.NotSupported
+
void StartNotify()
Starts a notification session from this characteristic
if it supports value notifications or indications.
Possible Errors: org.bluez.Error.Failed
+ org.bluez.Error.NotPermitted
org.bluez.Error.InProgress
org.bluez.Error.NotSupported
Possible Errors: org.bluez.Error.Failed
+ void Confirm() [optional] (Server only)
+
+ This method doesn't expect a reply so it is just a
+ confirmation that value was received.
+
+ Possible Errors: org.bluez.Error.Failed
+
Properties string UUID [read-only]
128-bit characteristic UUID.
when a notification or indication is received, upon
which a PropertiesChanged signal will be emitted.
+ boolean WriteAcquired [read-only, optional]
+
+ True, if this characteristic has been acquired by any
+ client using AcquireWrite.
+
+ For client properties is ommited in case
+ 'write-without-response' flag is not set.
+
+ For server the presence of this property indicates
+ that AcquireWrite is supported.
+
+ boolean NotifyAcquired [read-only, optional]
+
+ True, if this characteristic has been acquired by any
+ client using AcquireNotify.
+
+ For client this properties is ommited in case 'notify'
+ flag is not set.
+
+ For server the presence of this property indicates
+ that AcquireNotify is supported.
+
boolean Notifying [read-only, optional]
True, if notifications or indications on this
Possible options: "offset": Start offset
"device": Device path (Server only)
+ "link": Link type (Server only)
Possible Errors: org.bluez.Error.Failed
org.bluez.Error.InProgress
Possible options: "offset": Start offset
"device": Device path (Server only)
+ "link": Link type (Server only)
Possible Errors: org.bluez.Error.Failed
org.bluez.Error.InProgress
Linux kernel v4.5 Version 1.11
Linux kernel v4.6 Version 1.12
Linux kernel v4.8 Version 1.13
-Linux kernel v4.9 Version 1.14 (not yet released)
+Linux kernel v4.9 Version 1.14
Version 1.1 introduces Set Device ID command.
+++ /dev/null
-BlueZ D-Bus Out Of Band Pairing API description
-===============================================
-
-Copyright (C) 2011 Szymon Janc <szymon.janc@tieto.com> for ST-Ericsson
-
-Service org.bluez
-Interface org.bluez.OutOfBand
-Object path [variable prefix]/{hci0,hci1,...}
-
-Methods array{byte} hash, array{byte} randomizer ReadLocalData()
-
- This method reads local OOB data from adapter. Return
- value is pair of arrays 16 bytes each.
-
- Note: This method will generate and return new local
- OOB data.
-
- Possible errors: org.bluez.Error.Failed
- org.bluez.Error.InProgress
-
- void AddRemoteData(string address, array{byte} hash,
- array{byte} randomizer)
-
- This method adds new Out Of Band data for
- specified address. If data for specified address
- already exists it will be overwritten with new one.
-
- Possible errors: org.bluez.Error.Failed
- org.bluez.Error.InvalidArguments
-
- void RemoveRemoteData(string address)
-
- This method removes Out Of Band data for specified
- address. If data for specified address does not exist
- nothing is removed.
-
- Possible errors: org.bluez.Error.Failed
- org.bluez.Error.InvalidArguments
CONFIG_DEVTMPFS=y
CONFIG_DEBUG_FS=y
+For Bluetooth functionality:
+
+ CONFIG_BT=y
+ CONFIG_BT_BREDR=y
+ CONFIG_BT_RFCOMM=y
+ CONFIG_BT_BNEP=y
+ CONFIG_BT_HIDP=y
+ CONFIG_BT_LE=y
+
+ CONFIG_BT_HCIVHCI=y
+
+ CONFIG_CRYPTO_CMAC=y
+ CONFIG_CRYPTO_USER_API=y
+ CONFIG_CRYPTO_USER_API_HASH=y
+ CONFIG_CRYPTO_USER_API_SKCIPHER=y
+
+ CONFIG_UNIX=y
+
+ CONFIG_UHID=y
+
+
These options should be installed as .config in the kernel source directory
followed by this command.
switch (btdev->type) {
case BTDEV_TYPE_BREDRLE:
- btdev->version = 0x08;
+ btdev->version = 0x09;
set_bredrle_features(btdev);
set_bredrle_commands(btdev);
break;
set_bredr_commands(btdev);
break;
case BTDEV_TYPE_LE:
- btdev->version = 0x08;
+ btdev->version = 0x09;
set_le_features(btdev);
set_le_commands(btdev);
break;
#define MAX_RX_LEN 0x00fb
#define MAX_RX_TIME 0x0848
+#define DEFAULT_ALL_PHYS 0x03
+#define DEFAULT_TX_PHYS 0x00
+#define DEFAULT_RX_PHYS 0x00
+
struct bt_peer {
uint8_t addr_type;
uint8_t addr[6];
uint8_t le_resolv_enable;
uint16_t le_resolv_timeout;
+ uint8_t le_default_all_phys;
+ uint8_t le_default_tx_phys;
+ uint8_t le_default_rx_phys;
+
struct bt_peer scan_cache[SCAN_CACHE_SIZE];
uint8_t scan_cache_count;
};
hci->commands[35] |= 0x02; /* LE Set Address Resolution Enable */
hci->commands[35] |= 0x04; /* LE Set Resolvable Private Address Timeout */
hci->commands[35] |= 0x08; /* LE Read Maximum Data Length */
+ hci->commands[35] |= 0x10; /* LE Read PHY */
+ hci->commands[35] |= 0x20; /* LE Set Default PHY */
+ hci->commands[35] |= 0x40; /* LE Set PHY */
+ //hci->commands[35] |= 0x80; /* LE Enhanced Receiver Test */
+ //hci->commands[36] |= 0x01; /* LE Enhanced Transmitter Test */
+ //hci->commands[36] |= 0x02; /* LE Set Advertising Set Random Address */
+ //hci->commands[36] |= 0x04; /* LE Set Extended Advertising Parameters */
+ //hci->commands[36] |= 0x08; /* LE Set Extended Advertising Data */
+ //hci->commands[36] |= 0x10; /* LE Set Extended Scan Response Data */
+ //hci->commands[36] |= 0x20; /* LE Set Extended Advertising Enable */
+ //hci->commands[36] |= 0x40; /* LE Read Maximum Advertising Data Length */
+ //hci->commands[36] |= 0x80; /* LE Read Number of Supported Advertising Sets */
+ //hci->commands[37] |= 0x01; /* LE Remove Advertising Set */
+ //hci->commands[37] |= 0x02; /* LE Clear Advertising Sets */
+ //hci->commands[37] |= 0x04; /* LE Set Periodic Advertising Parameters */
+ //hci->commands[37] |= 0x08; /* LE Set Periodic Advertising Data */
+ //hci->commands[37] |= 0x10; /* LE Set Periodic Advertising Enable */
+ //hci->commands[37] |= 0x20; /* LE Set Extended Scan Parameters */
+ //hci->commands[37] |= 0x40; /* LE Set Extended Scan Enable */
+ //hci->commands[37] |= 0x80; /* LE Extended Create Connection */
+ //hci->commands[38] |= 0x01; /* LE Periodic Advertising Create Sync */
+ //hci->commands[38] |= 0x02; /* LE Periodic Advertising Create Sync Cancel */
+ //hci->commands[38] |= 0x04; /* LE Periodic Advertising Terminate Sync */
+ //hci->commands[38] |= 0x08; /* LE Add Device To Periodic Advertiser List */
+ //hci->commands[38] |= 0x10; /* LE Remove Device From Periodic Advertiser List */
+ //hci->commands[38] |= 0x20; /* LE Clear Periodic Advertiser List */
+ //hci->commands[38] |= 0x40; /* LE Read Periodic Advertiser List Size */
+ //hci->commands[38] |= 0x80; /* LE Read Transmit Power */
+ //hci->commands[39] |= 0x01; /* LE Read RF Path Compensation */
+ //hci->commands[39] |= 0x02; /* LE Write RF Path Compensation */
+ //hci->commands[39] |= 0x04; /* LE Set Privacy Mode */
memset(hci->features, 0, sizeof(hci->features));
hci->features[4] |= 0x20; /* BR/EDR Not Supported */
//hci->le_event_mask[1] |= 0x01; /* LE Generate DHKey Complete */
//hci->le_event_mask[1] |= 0x02; /* LE Enhanced Connection Complete */
//hci->le_event_mask[1] |= 0x04; /* LE Direct Advertising Report */
+ //hci->le_event_mask[1] |= 0x08; /* LE PHY Update Complete */
+ //hci->le_event_mask[1] |= 0x10; /* LE Extended Advertising Report */
+ //hci->le_event_mask[1] |= 0x20; /* LE Periodic Advertising Sync Established */
+ //hci->le_event_mask[1] |= 0x40; /* LE Periodic Advertising Report */
+ //hci->le_event_mask[1] |= 0x80; /* LE Periodic Advertising Sync Lost */
+ //hci->le_event_mask[2] |= 0x01; /* LE Extended Scan Timeout */
+ //hci->le_event_mask[2] |= 0x02; /* LE Extended Advertising Set Terminated */
+ //hci->le_event_mask[2] |= 0x04; /* LE Scan Request Received */
+ //hci->le_event_mask[2] |= 0x08; /* LE Channel Selection Algorithm */
hci->le_mtu = 64;
hci->le_max_pkt = 1;
hci->le_features[0] |= 0x20; /* LE Data Packet Length Extension */
hci->le_features[0] |= 0x40; /* LL Privacy */
hci->le_features[0] |= 0x80; /* Extended Scanner Filter Policies */
+ hci->le_features[1] |= 0x01; /* LE 2M PHY */
+ hci->le_features[1] |= 0x02; /* Stable Modulation Index - Transmitter */
+ hci->le_features[1] |= 0x04; /* Stable Modulation Index - Receiver */
+ hci->le_features[1] |= 0x08; /* LE Coded PHY */
+ //hci->le_features[1] |= 0x10; /* LE Extended Advertising */
+ //hci->le_features[1] |= 0x20; /* LE Periodic Advertising */
+ hci->le_features[1] |= 0x40; /* Channel Selection Algorithm #2 */
+ hci->le_features[1] |= 0x80; /* LE Power Class 1 */
+ hci->le_features[2] |= 0x01; /* Minimum Number of Used Channels Procedure */
memset(hci->le_random_addr, 0, sizeof(hci->le_random_addr));
clear_resolv_list(hci);
hci->le_resolv_enable = 0x00;
hci->le_resolv_timeout = 0x0384; /* 900 secs or 15 minutes */
+
+ hci->le_default_all_phys = DEFAULT_ALL_PHYS;
+ hci->le_default_tx_phys = DEFAULT_TX_PHYS;
+ hci->le_default_rx_phys = DEFAULT_RX_PHYS;
}
static void clear_scan_cache(struct bt_le *hci)
struct bt_hci_rsp_read_local_version rsp;
rsp.status = BT_HCI_ERR_SUCCESS;
- rsp.hci_ver = 0x08;
+ rsp.hci_ver = 0x09;
rsp.hci_rev = cpu_to_le16(0x0000);
- rsp.lmp_ver = 0x08;
+ rsp.lmp_ver = 0x09;
rsp.manufacturer = cpu_to_le16(hci->manufacturer);
rsp.lmp_subver = cpu_to_le16(0x0000);
&rsp, sizeof(rsp));
}
+static void cmd_le_read_phy(struct bt_le *hci, const void *data, uint8_t size)
+{
+ const struct bt_hci_cmd_le_read_phy *cmd = data;
+ struct bt_hci_rsp_le_read_phy rsp;
+
+ rsp.status = BT_HCI_ERR_SUCCESS;
+ rsp.handle = cmd->handle;
+ rsp.tx_phy = 0x01; /* LE 1M */
+ rsp.rx_phy = 0x01; /* LE 1M */
+
+ cmd_complete(hci, BT_HCI_CMD_LE_READ_PHY, &rsp, sizeof(rsp));
+}
+
+static void cmd_le_set_default_phy(struct bt_le *hci,
+ const void *data, uint8_t size)
+{
+ const struct bt_hci_cmd_le_set_default_phy *cmd = data;
+ uint8_t status, tx_phys, rx_phys;
+ uint8_t phys_mask;
+
+ phys_mask = (true << 0) | ((!!(hci->le_features[1] & 0x01)) << 1)
+ | ((!!(hci->le_features[1] & 0x08)) << 2);
+
+ if (cmd->all_phys > 0x03) {
+ cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS,
+ BT_HCI_CMD_LE_SET_DEFAULT_PHY);
+ return;
+ }
+
+ /* Transmitter PHYs preferences */
+ if (!(cmd->all_phys & 0x01)) {
+ /* At least one preference bit shall be set to 1 */
+ if (!cmd->tx_phys) {
+ cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS,
+ BT_HCI_CMD_LE_SET_DEFAULT_PHY);
+ return;
+ }
+
+ if (cmd->tx_phys & ~phys_mask) {
+ cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS,
+ BT_HCI_CMD_LE_SET_DEFAULT_PHY);
+ return;
+ }
+
+ tx_phys = cmd->tx_phys;
+ } else
+ tx_phys = 0x00;
+
+ /* Transmitter PHYs preferences */
+ if (!(cmd->all_phys & 0x02)) {
+ /* At least one preference bit shall be set to 1 */
+ if (!cmd->rx_phys) {
+ cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS,
+ BT_HCI_CMD_LE_SET_DEFAULT_PHY);
+ return;
+ }
+
+ if (cmd->rx_phys & ~phys_mask) {
+ cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS,
+ BT_HCI_CMD_LE_SET_DEFAULT_PHY);
+ return;
+ }
+
+ rx_phys = cmd->rx_phys;
+ } else
+ rx_phys = 0x00;
+
+ hci->le_default_all_phys = cmd->all_phys;
+ hci->le_default_tx_phys = tx_phys;
+ hci->le_default_rx_phys = rx_phys;
+
+ status = BT_HCI_ERR_SUCCESS;
+ cmd_complete(hci, BT_HCI_CMD_LE_SET_DEFAULT_PHY,
+ &status, sizeof(status));
+}
+
+static void cmd_le_set_phy(struct bt_le *hci, const void *data, uint8_t size)
+{
+ const struct bt_hci_cmd_le_set_phy *cmd = data;
+ struct bt_hci_evt_le_phy_update_complete evt;
+
+ cmd_status(hci, BT_HCI_ERR_SUCCESS, BT_HCI_CMD_LE_SET_PHY);
+
+ evt.status = BT_HCI_ERR_SUCCESS;
+ evt.handle = cmd->handle;
+ evt.tx_phy = 0x01; /* LE 1M */
+ evt.rx_phy = 0x01; /* LE 1M */
+
+ if (hci->le_event_mask[1] & 0x08)
+ le_meta_event(hci, BT_HCI_EVT_LE_PHY_UPDATE_COMPLETE,
+ &evt, sizeof(evt));
+}
+
static const struct {
uint16_t opcode;
void (*func) (struct bt_le *hci, const void *data, uint8_t size);
cmd_le_set_resolv_timeout, 2, true },
{ BT_HCI_CMD_LE_READ_MAX_DATA_LENGTH,
cmd_le_read_max_data_length, 0, true },
+ { BT_HCI_CMD_LE_READ_PHY,
+ cmd_le_read_phy, 2, true },
+ { BT_HCI_CMD_LE_SET_DEFAULT_PHY,
+ cmd_le_set_default_phy, 3, true },
+ { BT_HCI_CMD_LE_SET_PHY,
+ cmd_le_set_phy, 7, true },
{ }
};
void *prop_data;
GDBusProxyFunction removed_func;
void *removed_data;
+ DBusPendingCall *get_all_call;
+ gboolean pending;
};
struct prop_entry {
}
}
+static void proxy_added(GDBusClient *client, GDBusProxy *proxy)
+{
+ if (!proxy->pending)
+ return;
+
+ if (client->proxy_added)
+ client->proxy_added(proxy, client->user_data);
+
+ proxy->pending = FALSE;
+}
+
static void get_all_properties_reply(DBusPendingCall *call, void *user_data)
{
GDBusProxy *proxy = user_data;
DBusMessageIter iter;
DBusError error;
+ g_dbus_client_ref(client);
+
dbus_error_init(&error);
if (dbus_set_error_from_message(&error, reply) == TRUE) {
update_properties(proxy, &iter, FALSE);
done:
- if (g_list_find(client->proxy_list, proxy) == NULL) {
- if (client->proxy_added)
- client->proxy_added(proxy, client->user_data);
-
- client->proxy_list = g_list_append(client->proxy_list, proxy);
- }
+ proxy_added(client, proxy);
dbus_message_unref(reply);
+ dbus_pending_call_unref(proxy->get_all_call);
+ proxy->get_all_call = NULL;
+
g_dbus_client_unref(client);
}
GDBusClient *client = proxy->client;
const char *service_name = client->service_name;
DBusMessage *msg;
- DBusPendingCall *call;
+
+ if (proxy->get_all_call)
+ return;
msg = dbus_message_new_method_call(service_name, proxy->obj_path,
DBUS_INTERFACE_PROPERTIES, "GetAll");
DBUS_TYPE_INVALID);
if (g_dbus_send_message_with_reply(client->dbus_conn, msg,
- &call, -1) == FALSE) {
+ &proxy->get_all_call, -1) == FALSE) {
dbus_message_unref(msg);
return;
}
- g_dbus_client_ref(client);
-
- dbus_pending_call_set_notify(call, get_all_properties_reply,
- proxy, NULL);
- dbus_pending_call_unref(call);
+ dbus_pending_call_set_notify(proxy->get_all_call,
+ get_all_properties_reply, proxy, NULL);
dbus_message_unref(msg);
}
-static GDBusProxy *proxy_lookup(GDBusClient *client, const char *path,
+static GDBusProxy *proxy_lookup(GList *list, const char *path,
const char *interface)
{
- GList *list;
+ GList *l;
- for (list = g_list_first(client->proxy_list); list;
- list = g_list_next(list)) {
- GDBusProxy *proxy = list->data;
+ for (l = g_list_first(list); l; l = g_list_next(l)) {
+ GDBusProxy *proxy = l->data;
if (g_str_equal(proxy->interface, interface) == TRUE &&
g_str_equal(proxy->obj_path, path) == TRUE)
proxy->interface,
properties_changed,
proxy, NULL);
+ proxy->pending = TRUE;
+
+ client->proxy_list = g_list_append(client->proxy_list, proxy);
return g_dbus_proxy_ref(proxy);
}
if (proxy->client) {
GDBusClient *client = proxy->client;
+ if (proxy->get_all_call != NULL) {
+ dbus_pending_call_cancel(proxy->get_all_call);
+ dbus_pending_call_unref(proxy->get_all_call);
+ proxy->get_all_call = NULL;
+ }
+
if (client->proxy_removed)
client->proxy_removed(proxy, client->user_data);
}
}
+static void start_service(GDBusProxy *proxy)
+{
+ GDBusClient *client = proxy->client;
+ const char *service_name = client->service_name;
+ dbus_uint32_t flags = 0;
+ DBusMessage *msg;
+
+ msg = dbus_message_new_method_call(DBUS_SERVICE_DBUS, DBUS_PATH_DBUS,
+ DBUS_INTERFACE_DBUS,
+ "StartServiceByName");
+ if (msg == NULL)
+ return;
+
+ dbus_message_append_args(msg, DBUS_TYPE_STRING, &service_name,
+ DBUS_TYPE_UINT32, &flags,
+ DBUS_TYPE_INVALID);
+
+ g_dbus_send_message(client->dbus_conn, msg);
+ return;
+}
+
GDBusProxy *g_dbus_proxy_new(GDBusClient *client, const char *path,
const char *interface)
{
if (client == NULL)
return NULL;
- proxy = proxy_lookup(client, path, interface);
+ proxy = proxy_lookup(client->proxy_list, path, interface);
if (proxy)
return g_dbus_proxy_ref(proxy);
if (proxy == NULL)
return NULL;
- get_all_properties(proxy);
+ if (!client->connected) {
+ /* Force service to start */
+ start_service(proxy);
+ return g_dbus_proxy_ref(proxy);
+ }
+
+ if (!client->get_objects_call)
+ get_all_properties(proxy);
return g_dbus_proxy_ref(proxy);
}
if (__sync_sub_and_fetch(&proxy->ref_count, 1) > 0)
return;
+ if (proxy->get_all_call != NULL) {
+ dbus_pending_call_cancel(proxy->get_all_call);
+ dbus_pending_call_unref(proxy->get_all_call);
+ }
+
g_hash_table_destroy(proxy->prop_list);
g_free(proxy->obj_path);
return TRUE;
}
-static void refresh_properties(GDBusClient *client)
+static void refresh_properties(GList *list)
{
- GList *list;
+ GList *l;
- for (list = g_list_first(client->proxy_list); list;
- list = g_list_next(list)) {
+ for (l = g_list_first(list); l; l = g_list_next(l)) {
GDBusProxy *proxy = list->data;
- get_all_properties(proxy);
+ if (proxy->pending)
+ get_all_properties(proxy);
}
}
if (g_str_equal(interface, DBUS_INTERFACE_PROPERTIES) == TRUE)
return;
- proxy = proxy_lookup(client, path, interface);
- if (proxy) {
+ proxy = proxy_lookup(client->proxy_list, path, interface);
+ if (proxy && !proxy->pending) {
update_properties(proxy, iter, FALSE);
return;
}
- proxy = proxy_new(client, path, interface);
- if (proxy == NULL)
- return;
+ if (!proxy) {
+ proxy = proxy_new(client, path, interface);
+ if (proxy == NULL)
+ return;
+ }
update_properties(proxy, iter, FALSE);
- if (client->proxy_added)
- client->proxy_added(proxy, client->user_data);
-
- client->proxy_list = g_list_append(client->proxy_list, proxy);
+ proxy_added(client, proxy);
}
static void parse_interfaces(GDBusClient *client, const char *path,
dbus_pending_call_unref(client->get_objects_call);
client->get_objects_call = NULL;
+ refresh_properties(client->proxy_list);
+
g_dbus_client_unref(client);
}
if ((!client->proxy_added && !client->proxy_removed) ||
!client->root_path) {
- refresh_properties(client);
+ refresh_properties(client->proxy_list);
return;
}
client->connected = TRUE;
+ get_managed_objects(client);
+
if (client->connect_func)
client->connect_func(conn, client->connect_data);
- get_managed_objects(client);
-
g_dbus_client_unref(client);
}
DBusMessageIter iter, dict, array;
GSList *invalidated;
- data->pending_prop = FALSE;
-
if (iface->pending_prop == NULL)
return;
{
GSList *l;
+ data->pending_prop = FALSE;
+
for (l = data->interfaces; l != NULL; l = l->next) {
struct interface_data *iface = l->data;
GHashTableIter iter;
gpointer key, value;
+ if (!apparam)
+ return 0;
+
g_hash_table_iter_init(&iter, apparam->tags);
while (g_hash_table_iter_next(&iter, &key, &value)) {
struct apparam_tag *tag = value;
}
GObexPacket *g_obex_packet_new_valist(guint8 opcode, gboolean final,
- guint8 first_hdr_id, va_list args)
+ guint first_hdr_id, va_list args)
{
GObexPacket *pkt;
}
GObexPacket *g_obex_packet_new(guint8 opcode, gboolean final,
- guint8 first_hdr_id, ...)
+ guint first_hdr_id, ...)
{
GObexPacket *pkt;
va_list args;
GObexDataPolicy data_policy);
const void *g_obex_packet_get_data(GObexPacket *pkt, gsize *len);
GObexPacket *g_obex_packet_new(guint8 opcode, gboolean final,
- guint8 first_hdr_id, ...);
+ guint first_hdr_id, ...);
GObexPacket *g_obex_packet_new_valist(guint8 opcode, gboolean final,
- guint8 first_hdr_id, va_list args);
+ guint first_hdr_id, va_list args);
void g_obex_packet_free(GObexPacket *pkt);
GObexPacket *g_obex_packet_decode(const void *data, gsize len,
guint g_obex_put_req(GObex *obex, GObexDataProducer data_func,
GObexFunc complete_func, gpointer user_data,
- GError **err, guint8 first_hdr_id, ...)
+ GError **err, guint first_hdr_id, ...)
{
GObexPacket *req;
va_list args;
guint g_obex_put_rsp(GObex *obex, GObexPacket *req,
GObexDataConsumer data_func, GObexFunc complete_func,
gpointer user_data, GError **err,
- guint8 first_hdr_id, ...)
+ guint first_hdr_id, ...)
{
struct transfer *transfer;
va_list args;
guint g_obex_get_req(GObex *obex, GObexDataConsumer data_func,
GObexFunc complete_func, gpointer user_data,
- GError **err, guint8 first_hdr_id, ...)
+ GError **err, guint first_hdr_id, ...)
{
struct transfer *transfer;
GObexPacket *req;
guint g_obex_get_rsp(GObex *obex, GObexDataProducer data_func,
GObexFunc complete_func, gpointer user_data,
- GError **err, guint8 first_hdr_id, ...)
+ GError **err, guint first_hdr_id, ...)
{
GObexPacket *rsp;
va_list args;
}
gboolean g_obex_send_rsp(GObex *obex, guint8 rspcode, GError **err,
- guint8 first_hdr_type, ...)
+ guint first_hdr_type, ...)
{
GObexPacket *rsp;
va_list args;
}
#endif
guint g_obex_connect(GObex *obex, GObexResponseFunc func, gpointer user_data,
- GError **err, guint8 first_hdr_id, ...)
+ GError **err, guint first_hdr_id, ...)
{
GObexPacket *req;
struct connect_data data;
gboolean remove_callback);
gboolean g_obex_send_rsp(GObex *obex, guint8 rspcode, GError **err,
- guint8 first_hdr_type, ...);
+ guint first_hdr_type, ...);
void g_obex_set_disconnect_function(GObex *obex, GObexFunc func,
gpointer user_data);
/* High level client functions */
guint g_obex_connect(GObex *obex, GObexResponseFunc func, gpointer user_data,
- GError **err, guint8 first_hdr_id, ...);
+ GError **err, guint first_hdr_id, ...);
guint g_obex_disconnect(GObex *obex, GObexResponseFunc func, gpointer user_data,
GError **err);
guint g_obex_put_req(GObex *obex, GObexDataProducer data_func,
GObexFunc complete_func, gpointer user_data,
- GError **err, guint8 first_hdr_id, ...);
+ GError **err, guint first_hdr_id, ...);
guint g_obex_put_req_pkt(GObex *obex, GObexPacket *req,
GObexDataProducer data_func, GObexFunc complete_func,
guint g_obex_get_req(GObex *obex, GObexDataConsumer data_func,
GObexFunc complete_func, gpointer user_data,
- GError **err, guint8 first_hdr_id, ...);
+ GError **err, guint first_hdr_id, ...);
guint g_obex_get_req_pkt(GObex *obex, GObexPacket *req,
GObexDataConsumer data_func, GObexFunc complete_func,
guint g_obex_put_rsp(GObex *obex, GObexPacket *req,
GObexDataConsumer data_func, GObexFunc complete_func,
gpointer user_data, GError **err,
- guint8 first_hdr_id, ...);
+ guint first_hdr_id, ...);
guint g_obex_get_rsp(GObex *obex, GObexDataProducer data_func,
GObexFunc complete_func, gpointer user_data,
- GError **err, guint8 first_hdr_id, ...);
+ GError **err, guint first_hdr_id, ...);
guint g_obex_get_rsp_pkt(GObex *obex, GObexPacket *rsp,
GObexDataProducer data_func, GObexFunc complete_func,
{ "4.0", 0x06 },
{ "4.1", 0x07 },
{ "4.2", 0x08 },
+ { "5.0", 0x09 },
{ NULL }
};
#define AV_REMOTE_TARGET_PROFILE_ID AV_REMOTE_TARGET_SVCLASS_ID
#define ADVANCED_AUDIO_PROFILE_ID ADVANCED_AUDIO_SVCLASS_ID
#define AV_REMOTE_PROFILE_ID AV_REMOTE_SVCLASS_ID
-#define VIDEO_CONF_PROFILE_ID VIDEO_CONF_SVCLASS_ID
#define INTERCOM_PROFILE_ID INTERCOM_SVCLASS_ID
#define FAX_PROFILE_ID FAX_SVCLASS_ID
#define HEADSET_AGW_PROFILE_ID HEADSET_AGW_SVCLASS_ID
#define GATT_CHARAC_RECONNECTION_ADDRESS 0x2A03
#define GATT_CHARAC_PERIPHERAL_PREF_CONN 0x2A04
#define GATT_CHARAC_SERVICE_CHANGED 0x2A05
+#define GATT_CHARAC_BATTERY_LEVEL 0x2A19
#define GATT_CHARAC_SYSTEM_ID 0x2A23
#define GATT_CHARAC_MODEL_NUMBER_STRING 0x2A24
#define GATT_CHARAC_SERIAL_NUMBER_STRING 0x2A25
#define GATT_EXTERNAL_REPORT_REFERENCE 0x2907
#define GATT_REPORT_REFERENCE 0x2908
+/* GATT Mesh Services */
+#define MESH_PROV_SVC_UUID "00001827-0000-1000-8000-00805f9b34fb"
+#define MESH_PROXY_SVC_UUID "00001828-0000-1000-8000-00805f9b34fb"
+
+/* GATT Mesh Characteristic Types */
+#define MESH_PROVISIONING_DATA_IN 0x2ADB
+#define MESH_PROVISIONING_DATA_OUT 0x2ADC
+#define MESH_PROXY_DATA_IN 0x2ADD
+#define MESH_PROXY_DATA_OUT 0x2ADE
+
#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
/* GATT Service UUIDs : Defined by SIG */
#define GATT_IPSP_UUID 0x1820
--- /dev/null
+MeshCtl - BlueZ GATT based Bluetooth Mesh Provisioner
+******************************************
+
+Copyright (C) 2017 Intel Corporation. All rights reserved.
+
+Compilation and installation
+============================
+
+In addition to main BlueZ requirements, MeshCtl needs the following:
+ - JSON library
+
+Configuration and options
+=========================
+
+ --enable-mesh
+
+ Build meshctl and other Bluetooth Mesh based tools and utils
+
+Information
+===========
+
+Mailing lists:
+ linux-bluetooth@vger.kernel.org
+
+For additional information about the project visit BlueZ web site:
+ http://www.bluez.org
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2017 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>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <inttypes.h>
+
+#include <glib.h>
+
+#include <lib/bluetooth.h>
+
+#include "src/shared/shell.h"
+#include "mesh/util.h"
+#include "mesh/agent.h"
+
+struct input_request {
+ oob_type_t type;
+ uint16_t len;
+ agent_input_cb cb;
+ void *user_data;
+};
+
+static struct input_request pending_request = {NONE, 0, NULL, NULL};
+
+bool agent_completion(void)
+{
+ if (pending_request.type == NONE)
+ return false;
+
+ return true;
+}
+
+static void reset_input_request(void)
+{
+ pending_request.type = NONE;
+ pending_request.len = 0;
+ pending_request.cb = NULL;
+ pending_request.user_data = NULL;
+}
+
+static void response_hexadecimal(const char *input, void *user_data)
+{
+ uint8_t buf[MAX_HEXADECIMAL_OOB_LEN];
+
+ if (!str2hex(input, strlen(input), buf, pending_request.len) ) {
+ bt_shell_printf("Incorrect input: expecting %d hex octets\n",
+ pending_request.len);
+ return;
+ }
+
+ if (pending_request.cb)
+ pending_request.cb(HEXADECIMAL, buf, pending_request.len,
+ pending_request.user_data);
+
+ reset_input_request();
+}
+
+static void response_decimal(const char *input, void *user_data)
+{
+ uint8_t buf[DECIMAL_OOB_LEN];
+
+ if (strlen(input) > pending_request.len)
+ return;
+
+ bt_put_be32(atoi(input), buf);
+
+ if (pending_request.cb)
+ pending_request.cb(DECIMAL, buf, DECIMAL_OOB_LEN,
+ pending_request.user_data);
+
+ reset_input_request();
+}
+
+static void response_ascii(const char *input, void *user_data)
+{
+ if (pending_request.cb)
+ pending_request.cb(ASCII, (uint8_t *) input, strlen(input),
+ pending_request.user_data);
+
+ reset_input_request();
+}
+
+static bool request_hexadecimal(uint16_t len)
+{
+ if (len > MAX_HEXADECIMAL_OOB_LEN)
+ return false;
+
+ bt_shell_printf("Request hexadecimal key (hex %d octets)\n", len);
+ bt_shell_prompt_input("mesh", "Enter key (hex number):", response_hexadecimal,
+ NULL);
+
+ return true;
+}
+
+static uint32_t power_ten(uint8_t power)
+{
+ uint32_t ret = 1;
+
+ while (power--)
+ ret *= 10;
+
+ return ret;
+}
+
+static bool request_decimal(uint16_t len)
+{
+ bt_shell_printf("Request decimal key (0 - %d)\n", power_ten(len) - 1);
+ bt_shell_prompt_input("mesh", "Enter Numeric key:", response_decimal, NULL);
+
+ return true;
+}
+
+static bool request_ascii(uint16_t len)
+{
+ if (len > MAX_ASCII_OOB_LEN)
+ return false;
+
+ bt_shell_printf("Request ASCII key (max characters %d)\n", len);
+ bt_shell_prompt_input("mesh", "Enter key (ascii string):", response_ascii,
+ NULL);
+
+ return true;
+}
+
+bool agent_input_request(oob_type_t type, uint16_t max_len, agent_input_cb cb,
+ void *user_data)
+{
+ bool result;
+
+ if (pending_request.type != NONE)
+ return FALSE;
+
+ switch (type) {
+ case HEXADECIMAL:
+ result = request_hexadecimal(max_len);
+ break;
+ case DECIMAL:
+ result = request_decimal(max_len);
+ break;
+ case ASCII:
+ result = request_ascii(max_len);
+ break;
+ case NONE:
+ case OUTPUT:
+ default:
+ return false;
+ };
+
+ if (result) {
+ pending_request.type = type;
+ pending_request.len = max_len;
+ pending_request.cb = cb;
+ pending_request.user_data = user_data;
+
+ return true;
+ }
+
+ return false;
+}
+
+static void response_output(const char *input, void *user_data)
+{
+ reset_input_request();
+}
+
+bool agent_output_request(const char* str)
+{
+ if (pending_request.type != NONE)
+ return false;
+
+ pending_request.type = OUTPUT;
+ bt_shell_prompt_input("mesh", str, response_output, NULL);
+ return true;
+}
+
+void agent_output_request_cancel(void)
+{
+ if (pending_request.type != OUTPUT)
+ return;
+ pending_request.type = NONE;
+ bt_shell_release_prompt("");
+}
*
* BlueZ - Bluetooth protocol stack for Linux
*
- * Copyright (C) 2014 Intel Corporation. All rights reserved.
+ * Copyright (C) 2017 Intel Corporation. All rights reserved.
*
*
* This library is free software; you can redistribute it and/or
*
*/
-struct bt_hog;
+#define MAX_HEXADECIMAL_OOB_LEN 128
+#define DECIMAL_OOB_LEN 4
+#define MAX_ASCII_OOB_LEN 16
-struct bt_hog *bt_hog_new_default(const char *name, uint16_t vendor,
- uint16_t product, uint16_t version,
- void *primary);
+typedef enum {
+ NONE,
+ HEXADECIMAL,
+ DECIMAL,
+ ASCII,
+ OUTPUT,
+} oob_type_t;
-struct bt_hog *bt_hog_new(int fd, const char *name, uint16_t vendor,
- uint16_t product, uint16_t version,
- void *primary);
+typedef void (*agent_input_cb)(oob_type_t type, void *input, uint16_t len,
+ void *user_data);
+bool agent_input_request(oob_type_t type, uint16_t max_len, agent_input_cb cb,
+ void *user_data);
-struct bt_hog *bt_hog_ref(struct bt_hog *hog);
-void bt_hog_unref(struct bt_hog *hog);
-
-bool bt_hog_attach(struct bt_hog *hog, void *gatt);
-void bt_hog_detach(struct bt_hog *hog);
-
-int bt_hog_set_control_point(struct bt_hog *hog, bool suspend);
-int bt_hog_send_report(struct bt_hog *hog, void *data, size_t size, int type);
+bool agent_output_request(const char* str);
+void agent_output_request_cancel(void);
+bool agent_completion(void);
+bool agent_input(const char *input);
+void agent_release(void);
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2017 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>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <sys/uio.h>
+#include <wordexp.h>
+
+#include <glib.h>
+
+#include "src/shared/shell.h"
+#include "src/shared/util.h"
+#include "mesh/mesh-net.h"
+#include "mesh/keys.h"
+#include "mesh/net.h"
+#include "mesh/node.h"
+#include "mesh/prov-db.h"
+#include "mesh/util.h"
+#include "mesh/config-model.h"
+
+#define MIN_COMPOSITION_LEN 16
+
+static bool client_msg_recvd(uint16_t src, uint8_t *data,
+ uint16_t len, void *user_data)
+{
+ uint32_t opcode;
+ struct mesh_node *node;
+ uint16_t app_idx, net_idx, addr;
+ uint32_t mod_id;
+ uint16_t primary;
+ uint16_t ele_addr;
+ uint8_t ele_idx;
+ struct mesh_publication pub;
+ int n;
+ uint16_t i;
+
+ if (mesh_opcode_get(data, len, &opcode, &n)) {
+ len -= n;
+ data += n;
+ } else
+ return false;
+
+ if (IS_UNICAST(src)) {
+ node = node_find_by_addr(src);
+ } else
+ node = NULL;
+
+ if (!node)
+ return false;
+
+ primary = node_get_primary(node);
+ if (primary != src)
+ return false;
+
+ switch (opcode & ~OP_UNRELIABLE) {
+ default:
+ return false;
+
+ case OP_DEV_COMP_STATUS:
+ if (len < MIN_COMPOSITION_LEN || !node)
+ break;
+ if (node_parse_composition(node, data, len)) {
+ if (!prov_db_add_node_composition(node, data, len))
+ break;
+ }
+
+ if (node_get_composition(node))
+ prov_db_print_node_composition(node);
+ break;
+
+ case OP_APPKEY_STATUS:
+ if (len != 4)
+ break;
+
+ bt_shell_printf("Node %4.4x AppKey Status %s\n", src,
+ mesh_status_str(data[0]));
+ net_idx = get_le16(data + 1) & 0xfff;
+ app_idx = get_le16(data + 2) >> 4;
+
+ bt_shell_printf("\tNetKey %3.3x, AppKey %3.3x\n", net_idx, app_idx);
+
+ if (data[0] != MESH_STATUS_SUCCESS &&
+ data[0] != MESH_STATUS_IDX_ALREADY_STORED &&
+ node_app_key_delete(node, net_idx, app_idx))
+ prov_db_node_keys(node, node_get_app_keys(node),
+ "appKeys");
+ break;
+
+ case OP_NETKEY_STATUS:
+ if (len != 3)
+ break;
+
+ bt_shell_printf("Node %4.4x NetKey Status %s\n", src,
+ mesh_status_str(data[0]));
+ net_idx = get_le16(data + 1) & 0xfff;
+
+ bt_shell_printf("\tNetKey %3.3x\n", net_idx);
+
+ if (data[0] != MESH_STATUS_SUCCESS &&
+ data[0] != MESH_STATUS_IDX_ALREADY_STORED &&
+ node_net_key_delete(node, net_idx))
+ prov_db_node_keys(node, node_get_net_keys(node),
+ "netKeys");
+ break;
+
+ case OP_MODEL_APP_STATUS:
+ if (len != 7 && len != 9)
+ break;
+
+ bt_shell_printf("Node %4.4x Model App Status %s\n", src,
+ mesh_status_str(data[0]));
+ addr = get_le16(data + 1);
+ app_idx = get_le16(data + 3);
+
+ bt_shell_printf("\tElement %4.4x AppIdx %3.3x\n ", addr, app_idx);
+
+ if (len == 7) {
+ mod_id = get_le16(data + 5);
+ bt_shell_printf("ModelId %4.4x\n", mod_id);
+ mod_id = 0xffff0000 | mod_id;
+ } else {
+ mod_id = get_le16(data + 7);
+ bt_shell_printf("ModelId %4.4x %4.4x\n", get_le16(data + 5),
+ mod_id);
+ mod_id = get_le16(data + 5) << 16 | mod_id;
+ }
+
+ if (data[0] == MESH_STATUS_SUCCESS &&
+ node_add_binding(node, addr - src, mod_id, app_idx))
+ prov_db_add_binding(node, addr - src, mod_id, app_idx);
+ break;
+
+ case OP_NODE_IDENTITY_STATUS:
+ if (len != 4)
+ return true;
+ bt_shell_printf("Network index 0x%04x has "
+ "Node Identity state 0x%02x %s\n",
+ get_le16(data + 1), data[3],
+ mesh_status_str(data[0]));
+ break;
+
+ case OP_CONFIG_RELAY_STATUS:
+ if (len != 2)
+ return true;
+ bt_shell_printf("Node %4.4x Relay state: 0x%02x"
+ " count: %d steps: %d\n",
+ src, data[0], data[1]>>5, data[1] & 0x1f);
+ break;
+
+ case OP_CONFIG_PROXY_STATUS:
+ if (len != 1)
+ return true;
+ bt_shell_printf("Node %4.4x Proxy state: 0x%02x\n",
+ src, data[0]);
+ break;
+
+ case OP_CONFIG_DEFAULT_TTL_STATUS:
+ if (len != 1)
+ return true;
+ bt_shell_printf("Node %4.4x Default TTL %d\n", src, data[0]);
+ if (node_set_default_ttl (node, data[0]))
+ prov_db_node_set_ttl(node, data[0]);
+ break;
+
+ case OP_CONFIG_MODEL_PUB_STATUS:
+ if (len != 12 && len != 14)
+ return true;
+
+ bt_shell_printf("\nSet publication for node %4.4x status: %s\n",
+ src, data[0] == MESH_STATUS_SUCCESS ?
+ "Success" : mesh_status_str(data[0]));
+
+ if (data[0] != MESH_STATUS_SUCCESS)
+ return true;
+
+ ele_addr = get_le16(data + 1);
+ mod_id = get_le16(data + 10);
+ if (len == 14)
+ mod_id = (mod_id << 16) | get_le16(data + 12);
+ else
+ mod_id |= 0xffff0000;
+
+ pub.u.addr16 = get_le16(data + 3);
+ pub.app_idx = get_le16(data + 5);
+ pub.ttl = data[7];
+ pub.period = data[8];
+ n = (data[8] & 0x3f);
+ bt_shell_printf("Publication address: 0x%04x\n", pub.u.addr16);
+ switch (data[8] >> 6) {
+ case 0:
+ bt_shell_printf("Period: %d ms\n", n * 100);
+ break;
+ case 2:
+ n *= 10;
+ /* fall through */
+ case 1:
+ bt_shell_printf("Period: %d sec\n", n);
+ break;
+ case 3:
+ bt_shell_printf("Period: %d min\n", n * 10);
+ break;
+ }
+
+ pub.retransmit = data[9];
+ bt_shell_printf("Retransmit count: %d\n", data[9] >> 5);
+ bt_shell_printf("Retransmit Interval Steps: %d\n",
+ data[9] & 0x1f);
+
+ ele_idx = ele_addr - node_get_primary(node);
+
+ /* Local configuration is saved by server */
+ if (node == node_get_local_node())
+ break;
+
+ if (node_model_pub_set(node, ele_idx, mod_id, &pub))
+ prov_db_node_set_model_pub(node, ele_idx, mod_id,
+ node_model_pub_get(node, ele_idx, mod_id));
+ break;
+
+ /* Per Mesh Profile 4.3.2.19 */
+ case OP_CONFIG_MODEL_SUB_STATUS:
+ bt_shell_printf("\nSubscription changed"
+ " for node %4.4x status: %s\n", src,
+ data[0] == MESH_STATUS_SUCCESS ? "Success" :
+ mesh_status_str(data[0]));
+
+ if (data[0] != MESH_STATUS_SUCCESS)
+ return true;
+
+ bt_shell_printf("Element Addr:\t%4.4x\n", get_le16(data + 1));
+ bt_shell_printf("Subscr Addr:\t%4.4x\n", get_le16(data + 3));
+ bt_shell_printf("Model ID:\t%4.4x\n", get_le16(data + 5));
+ break;
+
+ /* TODO */
+ /* Save subscription info in database */
+
+ /* Per Mesh Profile 4.3.2.27 */
+ case OP_CONFIG_MODEL_SUB_LIST:
+
+ bt_shell_printf("\nSubscription list for node %4.4x "
+ "length: %u status: %s\n", src, len,
+ data[0] == MESH_STATUS_SUCCESS ? "Success" :
+ mesh_status_str(data[0]));
+
+ if (data[0] != MESH_STATUS_SUCCESS)
+ return true;
+
+ bt_shell_printf("Element Addr:\t%4.4x\n", get_le16(data + 1));
+ bt_shell_printf("Model ID:\t%4.4x\n", get_le16(data + 3));
+
+ for (i = 5; i < len; i += 2)
+ bt_shell_printf("Subscr Addr:\t%4.4x\n",
+ get_le16(data + i));
+ break;
+
+ /* Per Mesh Profile 4.3.2.50 */
+ case OP_MODEL_APP_LIST:
+ bt_shell_printf("\nModel App Key list for node %4.4x "
+ "length: %u status: %s\n", src, len,
+ data[0] == MESH_STATUS_SUCCESS ? "Success" :
+ mesh_status_str(data[0]));
+
+ if (data[0] != MESH_STATUS_SUCCESS)
+ return true;
+
+ bt_shell_printf("Element Addr:\t%4.4x\n", get_le16(data + 1));
+ bt_shell_printf("Model ID:\t%4.4x\n", get_le16(data + 3));
+
+ for (i = 5; i < len; i += 2)
+ bt_shell_printf("Model App Key:\t%4.4x\n",
+ get_le16(data + i));
+ break;
+
+ /* Per Mesh Profile 4.3.2.63 */
+ case OP_CONFIG_HEARTBEAT_PUB_STATUS:
+ bt_shell_printf("\nSet heartbeat for node %4.4x status: %s\n",
+ src,
+ data[0] == MESH_STATUS_SUCCESS ? "Success" :
+ mesh_status_str(data[0]));
+
+ if (data[0] != MESH_STATUS_SUCCESS)
+ return true;
+
+ bt_shell_printf("Destination:\t%4.4x\n", get_le16(data + 1));
+ bt_shell_printf("Count:\t\t%2.2x\n", data[3]);
+ bt_shell_printf("Period:\t\t%2.2x\n", data[4]);
+ bt_shell_printf("TTL:\t\t%2.2x\n", data[5]);
+ bt_shell_printf("Features:\t%4.4x\n", get_le16(data + 6));
+ bt_shell_printf("Net_Idx:\t%4.4x\n", get_le16(data + 8));
+ break;
+ }
+
+ return true;
+}
+
+static uint32_t target;
+static uint32_t parms[8];
+
+static uint32_t read_input_parameters(int argc, char *argv[])
+{
+ uint32_t i;
+
+ if (!argc)
+ return 0;
+
+ --argc;
+ ++argv;
+
+ if (!argc || argv[0][0] == '\0')
+ return 0;
+
+ memset(parms, 0xff, sizeof(parms));
+
+ for (i = 0; i < sizeof(parms)/sizeof(parms[0]) && i < (unsigned) argc;
+ i++) {
+ sscanf(argv[i], "%x", &parms[i]);
+ if (parms[i] == 0xffffffff)
+ break;
+ }
+
+ return i;
+}
+
+static void cmd_set_node(int argc, char *argv[])
+{
+ uint32_t dst;
+ char *end;
+
+ dst = strtol(argv[1], &end, 16);
+ if (end != (argv[1] + 4)) {
+ bt_shell_printf("Bad unicast address %s: "
+ "expected format 4 digit hex\n", argv[1]);
+ target = UNASSIGNED_ADDRESS;
+ } else {
+ bt_shell_printf("Configuring node %4.4x\n", dst);
+ target = dst;
+ set_menu_prompt("config", argv[1]);
+ }
+}
+
+static bool config_send(uint8_t *buf, uint16_t len)
+{
+ struct mesh_node *node = node_get_local_node();
+ uint16_t primary;
+
+ if(!node)
+ return false;
+
+ primary = node_get_primary(node);
+ if (target != primary)
+ return net_access_layer_send(DEFAULT_TTL, primary,
+ target, APP_IDX_DEV, buf, len);
+
+ node_local_data_handler(primary, target, node_get_iv_index(node),
+ node_get_sequence_number(node), APP_IDX_DEV,
+ buf, len);
+ return true;
+
+}
+
+static void cmd_default(uint32_t opcode)
+{
+ uint16_t n;
+ uint8_t msg[32];
+
+ if (IS_UNASSIGNED(target)) {
+ bt_shell_printf("Destination not set\n");
+ return;
+ }
+
+ n = mesh_opcode_set(opcode, msg);
+
+ if (!config_send(msg, n))
+ bt_shell_printf("Failed to send command (opcode 0x%x)\n",
+ opcode);
+}
+
+static void cmd_get_composition(int argc, char *argv[])
+{
+ uint16_t n;
+ uint8_t msg[32];
+ struct mesh_node *node;
+
+ if (IS_UNASSIGNED(target)) {
+ bt_shell_printf("Destination not set\n");
+ return;
+ }
+
+ node = node_find_by_addr(target);
+
+ if (!node)
+ return;
+
+ n = mesh_opcode_set(OP_DEV_COMP_GET, msg);
+
+ /* By default, use page 0 */
+ msg[n++] = (read_input_parameters(argc, argv) == 1) ? parms[0] : 0;
+
+ if (!config_send(msg, n))
+ bt_shell_printf("Failed to send \"GET NODE COMPOSITION\"\n");
+}
+
+static void cmd_net_key(int argc, char *argv[], uint32_t opcode)
+{
+ uint16_t n;
+ uint8_t msg[32];
+ uint16_t net_idx;
+ uint8_t *key;
+ struct mesh_node *node;
+
+ if (IS_UNASSIGNED(target)) {
+ bt_shell_printf("Destination not set\n");
+ return;
+ }
+
+ n = mesh_opcode_set(opcode, msg);
+
+ if (read_input_parameters(argc, argv) != 1) {
+ bt_shell_printf("Bad arguments %s\n", argv[1]);
+ return;
+ }
+
+ node = node_find_by_addr(target);
+ if (!node) {
+ bt_shell_printf("Node %4.4x\n not found", target);
+ return;
+ }
+
+ net_idx = parms[0];
+
+ if (opcode != OP_NETKEY_DELETE) {
+
+ key = keys_net_key_get(net_idx, true);
+ if (!key) {
+ bt_shell_printf("Network key with index %4.4x not found\n",
+ net_idx);
+ return;
+ }
+
+ put_le16(net_idx, &msg[n]);
+ n += 2;
+
+ memcpy(msg + n, key, 16);
+ n += 16;
+ }
+
+ if (!config_send(msg, n)) {
+ bt_shell_printf("Failed to send \"%s NET KEY\"\n",
+ opcode == OP_NETKEY_ADD ? "ADD" : "DEL");
+ return;
+ }
+
+ if (opcode != OP_NETKEY_DELETE) {
+ if (node_net_key_add(node, net_idx))
+ prov_db_node_keys(node, node_get_net_keys(node),
+ "netKeys");
+ } else {
+ if (node_net_key_delete(node, net_idx))
+ prov_db_node_keys(node, node_get_net_keys(node),
+ "netKeys");
+ }
+
+}
+
+static void cmd_add_net_key(int argc, char *argv[])
+{
+ cmd_net_key(argc, argv, OP_NETKEY_ADD);
+}
+
+static void cmd_del_net_key(int argc, char *argv[])
+{
+ cmd_net_key(argc, argv, OP_NETKEY_DELETE);
+}
+
+static void cmd_app_key(int argc, char *argv[], uint32_t opcode)
+{
+ uint16_t n;
+ uint8_t msg[32];
+ uint16_t net_idx;
+ uint16_t app_idx;
+ uint8_t *key;
+ struct mesh_node *node;
+
+ if (IS_UNASSIGNED(target)) {
+ bt_shell_printf("Destination not set\n");
+ return;
+ }
+
+ if (read_input_parameters(argc, argv) != 1) {
+ bt_shell_printf("Bad arguments %s\n", argv[1]);
+ return;
+ }
+
+ node = node_find_by_addr(target);
+ if (!node) {
+ bt_shell_printf("Node %4.4x\n not found", target);
+ return;
+ }
+
+ n = mesh_opcode_set(opcode, msg);
+
+ app_idx = parms[0];
+ net_idx = keys_app_key_get_bound(app_idx);
+ if (net_idx == NET_IDX_INVALID) {
+ bt_shell_printf("App key with index %4.4x not found\n", app_idx);
+ return;
+ }
+
+ msg[n++] = net_idx & 0xf;
+ msg[n++] = ((net_idx >> 8) & 0xf) |
+ ((app_idx << 4) & 0xf0);
+ msg[n++] = app_idx >> 4;
+
+ if (opcode != OP_APPKEY_DELETE) {
+ key = keys_app_key_get(app_idx, true);
+ if (!key) {
+ bt_shell_printf("App key %4.4x not found\n", net_idx);
+ return;
+ }
+
+ memcpy(msg + n, key, 16);
+ n += 16;
+ }
+
+ if (!config_send(msg, n)) {
+ bt_shell_printf("Failed to send \"ADD %s KEY\"\n",
+ opcode == OP_APPKEY_ADD ? "ADD" : "DEL");
+ return;
+ }
+
+ if (opcode != OP_APPKEY_DELETE) {
+ if (node_app_key_add(node, app_idx))
+ prov_db_node_keys(node, node_get_app_keys(node),
+ "appKeys");
+ } else {
+ if (node_app_key_delete(node, net_idx, app_idx))
+ prov_db_node_keys(node, node_get_app_keys(node),
+ "appKeys");
+ }
+}
+
+static void cmd_add_app_key(int argc, char *argv[])
+{
+ cmd_app_key(argc, argv, OP_APPKEY_ADD);
+}
+
+static void cmd_del_app_key(int argc, char *argv[])
+{
+ cmd_app_key(argc, argv, OP_APPKEY_DELETE);
+}
+
+static bool verify_config_target(uint32_t dst)
+{
+ struct mesh_node *node;
+
+ if (IS_UNASSIGNED(dst)) {
+ bt_shell_printf("Destination not set\n");
+ return false;
+ }
+
+ node = node_find_by_addr(dst);
+ if (!node) {
+ bt_shell_printf("Node with unicast address %4.4x unknown\n", dst);
+ return false;
+ }
+
+ if (!node_get_composition(node)) {
+ bt_shell_printf("Node composition for %4.4x unknown\n", dst);
+ return false;
+ }
+
+ return true;
+}
+
+static void cmd_bind(int argc, char *argv[])
+{
+ uint16_t n;
+ uint8_t msg[32];
+ int parm_cnt;
+
+ if (!verify_config_target(target))
+ return;
+
+ parm_cnt = read_input_parameters(argc, argv);
+ if (parm_cnt != 3 && parm_cnt != 4) {
+ bt_shell_printf("Bad arguments\n");
+ return;
+ }
+
+ n = mesh_opcode_set(OP_MODEL_APP_BIND, msg);
+
+ put_le16(target + parms[0], msg + n);
+ n += 2;
+ put_le16(parms[1], msg + n);
+ n += 2;
+ if (parm_cnt == 4) {
+ put_le16(parms[3], msg + n);
+ put_le16(parms[2], msg + n + 2);
+ n += 4;
+ } else {
+ put_le16(parms[2], msg + n);
+ n += 2;
+ }
+
+ if (!config_send(msg, n))
+ bt_shell_printf("Failed to send \"MODEL APP BIND\"\n");
+}
+
+static void cmd_set_ident(int argc, char *argv[])
+{
+ uint16_t n;
+ uint8_t msg[2 + 3 + 4];
+ int parm_cnt;
+
+ if (!verify_config_target(target))
+ return;
+
+ n = mesh_opcode_set(OP_NODE_IDENTITY_SET, msg);
+
+ parm_cnt = read_input_parameters(argc, argv);
+ if (parm_cnt != 2) {
+ bt_shell_printf("bad arguments\n");
+ return;
+ }
+
+ put_le16(parms[0], msg + n);
+ n += 2;
+ msg[n++] = parms[1];
+
+ if (!config_send(msg, n))
+ bt_shell_printf("Failed to send \"SET IDENTITY\"\n");
+}
+
+static void cmd_get_ident(int argc, char *argv[])
+{
+ uint16_t n;
+ uint8_t msg[2 + 2 + 4];
+ int parm_cnt;
+
+ if (!verify_config_target(target))
+ return;
+
+ n = mesh_opcode_set(OP_NODE_IDENTITY_GET, msg);
+
+ parm_cnt = read_input_parameters(argc, argv);
+ if (parm_cnt != 1) {
+ bt_shell_printf("bad arguments\n");
+ return;
+ }
+
+ put_le16(parms[0], msg + n);
+ n += 2;
+
+ if (!config_send(msg, n))
+ bt_shell_printf("Failed to send \"GET IDENTITY\"\n");
+}
+
+static void cmd_set_proxy(int argc, char *argv[])
+{
+ uint16_t n;
+ uint8_t msg[2 + 1 + 4];
+ int parm_cnt;
+
+ if (!verify_config_target(target))
+ return;
+
+ n = mesh_opcode_set(OP_CONFIG_PROXY_SET, msg);
+
+ parm_cnt = read_input_parameters(argc, argv);
+ if (parm_cnt != 1) {
+ bt_shell_printf("bad arguments");
+ return;
+ }
+
+ msg[n++] = parms[0];
+ msg[n++] = parms[1];
+
+ if (!config_send(msg, n))
+ bt_shell_printf("Failed to send \"SET PROXY\"\n");
+}
+
+static void cmd_get_proxy(int argc, char *argv[])
+{
+ cmd_default(OP_CONFIG_PROXY_GET);
+}
+
+static void cmd_set_relay(int argc, char *argv[])
+{
+ uint16_t n;
+ uint8_t msg[2 + 2 + 4];
+ int parm_cnt;
+
+ if (!verify_config_target(target))
+ return;
+
+ n = mesh_opcode_set(OP_CONFIG_RELAY_SET, msg);
+
+ parm_cnt = read_input_parameters(argc, argv);
+ if (parm_cnt != 3) {
+ bt_shell_printf("bad arguments\n");
+ return;
+ }
+
+ msg[n++] = parms[0];
+ msg[n++] = (parms[1] << 5) | parms[2];
+
+ if (!config_send(msg, n))
+ bt_shell_printf("Failed to send \"SET RELAY\"\n");
+}
+
+static void cmd_get_relay(int argc, char *argv[])
+{
+ cmd_default(OP_CONFIG_RELAY_GET);
+}
+
+static void cmd_set_ttl(int argc, char *argv[])
+{
+ uint16_t n;
+ uint8_t msg[32];
+ int parm_cnt;
+ uint8_t ttl;
+
+ if (IS_UNASSIGNED(target)) {
+ bt_shell_printf("Destination not set\n");
+ return;
+ }
+
+ n = mesh_opcode_set(OP_CONFIG_DEFAULT_TTL_SET, msg);
+
+ parm_cnt = read_input_parameters(argc, argv);
+ if (parm_cnt) {
+ ttl = parms[0] & TTL_MASK;
+ } else
+ ttl = node_get_default_ttl(node_get_local_node());
+
+ msg[n++] = ttl;
+
+ if (!config_send(msg, n))
+ bt_shell_printf("Failed to send \"SET_DEFAULT TTL\"\n");
+}
+
+static void cmd_set_pub(int argc, char *argv[])
+{
+ uint16_t n;
+ uint8_t msg[32];
+ int parm_cnt;
+
+ if (!verify_config_target(target))
+ return;
+
+ n = mesh_opcode_set(OP_CONFIG_MODEL_PUB_SET, msg);
+
+ parm_cnt = read_input_parameters(argc, argv);
+ if (parm_cnt != 6 && parm_cnt != 7) {
+ bt_shell_printf("Bad arguments\n");
+ return;
+ }
+
+ put_le16(parms[0], msg + n);
+ n += 2;
+ /* Publish address */
+ put_le16(parms[1], msg + n);
+ n += 2;
+ /* App key index + credential (set to 0) */
+ put_le16(parms[2], msg + n);
+ n += 2;
+ /* TTL */
+ msg[n++] = DEFAULT_TTL;
+ /* Publish period step count and step resolution */
+ msg[n++] = parms[3];
+ /* Publish retransmit count & interval steps */
+ msg[n++] = parms[4];
+ /* Model Id */
+ if (parm_cnt == 7) {
+ put_le16(parms[6], msg + n);
+ put_le16(parms[5], msg + n + 2);
+ n += 4;
+ } else {
+ put_le16(parms[5], msg + n);
+ n += 2;
+ }
+
+ if (!config_send(msg, n))
+ bt_shell_printf("Failed to send \"SET MODEL PUBLICATION\"\n");
+}
+
+static void cmd_get_pub(int argc, char *argv[])
+{
+ uint16_t n;
+ uint8_t msg[32];
+ int parm_cnt;
+
+ if (IS_UNASSIGNED(target)) {
+ bt_shell_printf("Destination not set\n");
+ return;
+ }
+
+ n = mesh_opcode_set(OP_CONFIG_MODEL_PUB_GET, msg);
+
+ parm_cnt = read_input_parameters(argc, argv);
+ if (parm_cnt != 2 && parm_cnt != 3) {
+ bt_shell_printf("Bad arguments: %s\n", argv[1]);
+ return;
+ }
+
+ /* Element Address */
+ put_le16(parms[0], msg + n);
+ n += 2;
+ /* Model Id */
+ if (parm_cnt == 3) {
+ put_le16(parms[2], msg + n);
+ put_le16(parms[1], msg + n + 2);
+ n += 4;
+ } else {
+ put_le16(parms[1], msg + n);
+ n += 2;
+ }
+
+ if (!config_send(msg, n))
+ bt_shell_printf("Failed to send \"GET MODEL PUBLICATION\"\n");
+}
+
+static void cmd_sub_add(int argc, char *argv[])
+{
+ uint16_t n;
+ uint8_t msg[32];
+ int parm_cnt;
+
+ if (IS_UNASSIGNED(target)) {
+ bt_shell_printf("Destination not set\n");
+ return;
+ }
+
+ n = mesh_opcode_set(OP_CONFIG_MODEL_SUB_ADD, msg);
+
+ parm_cnt = read_input_parameters(argc, argv);
+ if (parm_cnt != 3) {
+ bt_shell_printf("Bad arguments: %s\n", argv[1]);
+ return;
+ }
+
+ /* Per Mesh Profile 4.3.2.19 */
+ /* Element Address */
+ put_le16(parms[0], msg + n);
+ n += 2;
+ /* Subscription Address */
+ put_le16(parms[1], msg + n);
+ n += 2;
+ /* SIG Model ID */
+ put_le16(parms[2], msg + n);
+ n += 2;
+
+ if (!config_send(msg, n))
+ bt_shell_printf("Failed to send \"ADD SUBSCRIPTION\"\n");
+}
+
+static void cmd_sub_get(int argc, char *argv[])
+{
+ uint16_t n;
+ uint8_t msg[32];
+ int parm_cnt;
+
+ if (IS_UNASSIGNED(target)) {
+ bt_shell_printf("Destination not set\n");
+ return;
+ }
+
+ n = mesh_opcode_set(OP_CONFIG_MODEL_SUB_GET, msg);
+
+ parm_cnt = read_input_parameters(argc, argv);
+ if (parm_cnt != 2) {
+ bt_shell_printf("Bad arguments: %s\n", argv[1]);
+ return;
+ }
+
+ /* Per Mesh Profile 4.3.2.27 */
+ /* Element Address */
+ put_le16(parms[0], msg + n);
+ n += 2;
+ /* Model ID */
+ put_le16(parms[1], msg + n);
+ n += 2;
+
+ if (!config_send(msg, n))
+ bt_shell_printf("Failed to send \"GET SUB GET\"\n");
+}
+
+static void cmd_get_app(int argc, char *argv[])
+{
+ uint16_t n;
+ uint8_t msg[32];
+ int parm_cnt;
+
+ if (IS_UNASSIGNED(target)) {
+ bt_shell_printf("Destination not set\n");
+ return;
+ }
+
+ n = mesh_opcode_set(OP_MODEL_APP_GET, msg);
+
+ parm_cnt = read_input_parameters(argc, argv);
+ if (parm_cnt != 2) {
+ bt_shell_printf("Bad arguments: %s\n", argv[1]);
+ return;
+ }
+
+ /* Per Mesh Profile 4.3.2.49 */
+ /* Element Address */
+ put_le16(parms[0], msg + n);
+ n += 2;
+ /* Model ID */
+ put_le16(parms[1], msg + n);
+ n += 2;
+
+ if (!config_send(msg, n))
+ bt_shell_printf("Failed to send \"GET APP GET\"\n");
+}
+
+static void cmd_set_hb(int argc, char *argv[])
+{
+ uint16_t n;
+ uint8_t msg[32];
+ int parm_cnt;
+
+ if (IS_UNASSIGNED(target)) {
+ bt_shell_printf("Destination not set\n");
+ return;
+ }
+
+ n = mesh_opcode_set(OP_CONFIG_HEARTBEAT_PUB_SET, msg);
+
+ parm_cnt = read_input_parameters(argc, argv);
+ if (parm_cnt != 5) {
+ bt_shell_printf("Bad arguments: %s\n", argv[1]);
+ return;
+ }
+
+ /* Per Mesh Profile 4.3.2.62 */
+ /* Publish address */
+ put_le16(parms[0], msg + n);
+ n += 2;
+ /* Count Log */
+ msg[n++] = parms[1];
+ /* Period Log */
+ msg[n++] = parms[2];
+ /* Heartbeat TTL */
+ msg[n++] = DEFAULT_TTL;
+ /* Features */
+ put_le16(parms[3], msg + n);
+ n += 2;
+ /* NetKey Index */
+ put_le16(parms[4], msg + n);
+ n += 2;
+
+ if (!config_send(msg, n))
+ bt_shell_printf("Failed to send \"SET HEARTBEAT PUBLICATION\"\n");
+}
+
+static void cmd_get_ttl(int argc, char *argv[])
+{
+ cmd_default(OP_CONFIG_DEFAULT_TTL_GET);
+}
+
+static const struct bt_shell_menu cfg_menu = {
+ .name = "config",
+ .desc = "Configuration Model Submenu",
+ .entries = {
+ {"target", "<unicast>", cmd_set_node,
+ "Set target node to configure"},
+ {"composition-get", "[page_num]", cmd_get_composition,
+ "Get Composition Data"},
+ {"netkey-add", "<net_idx>", cmd_add_net_key,
+ "Add network key"},
+ {"netkey-del", "<net_idx>", cmd_del_net_key,
+ "Delete network key"},
+ {"appkey-add", "<app_idx>", cmd_add_app_key,
+ "Add application key"},
+ {"appkey-del", "<app_idx>", cmd_del_app_key,
+ "Delete application key"},
+ {"bind", "<ele_idx> <app_idx> <mod_id> [cid]",
+ cmd_bind, "Bind app key to a model"},
+ {"ttl-set", "<ttl>", cmd_set_ttl,
+ "Set default TTL"},
+ {"ttl-get", NULL, cmd_get_ttl,
+ "Get default TTL"},
+ {"pub-set", "<ele_addr> <pub_addr> <app_idx> "
+ "<per (step|res)> <re-xmt (cnt|per)> <mod id> "
+ "[cid]",
+ cmd_set_pub, "\n\t\t\t\t\t\t Set publication"},
+ {"pub-get", "<ele_addr> <model>", cmd_get_pub,
+ "Get publication"},
+ {"proxy-set", "<proxy>", cmd_set_proxy,
+ "Set proxy state"},
+ {"proxy-get", NULL, cmd_get_proxy,
+ "Get proxy state"},
+ {"ident-set", "<net_idx> <state>", cmd_set_ident,
+ "Set node identity state"},
+ {"ident-get", "<net_idx>", cmd_get_ident,
+ "Get node identity state"},
+ {"relay-set", "<relay> <rexmt count> <rexmt steps>",
+ cmd_set_relay,
+ "Set relay"},
+ {"relay-get", NULL, cmd_get_relay,
+ "Get relay"},
+ {"hb-pub-set", "<pub_addr> <count> <period> <features> <net_idx>",
+ cmd_set_hb, "Set heartbeati publish"},
+ {"sub-add", "<ele_addr> <sub_addr> <model id>",
+ cmd_sub_add, "Subscription add"},
+ {"sub-get", "<ele_addr> <model id>",
+ cmd_sub_get, "Subscription get"},
+ {"app-get", "<ele_addr> <model id>",
+ cmd_get_app, "Get App Keys"},
+ {} },
+};
+
+void config_client_get_composition(uint32_t dst)
+{
+ uint32_t tmp = target;
+
+ target = dst;
+ cmd_get_composition(0, NULL);
+ target = tmp;
+}
+
+static struct mesh_model_ops client_cbs = {
+ client_msg_recvd,
+ NULL,
+ NULL,
+ NULL
+};
+
+bool config_client_init(void)
+{
+ if (!node_local_model_register(PRIMARY_ELEMENT_IDX,
+ CONFIG_CLIENT_MODEL_ID,
+ &client_cbs, NULL))
+ return false;
+
+ bt_shell_add_submenu(&cfg_menu);
+
+ return true;
+}
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2017 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
+ *
+ */
+
+#define CONFIG_SERVER_MODEL_ID 0x0000
+#define CONFIG_CLIENT_MODEL_ID 0x0001
+
+#define OP_APPKEY_ADD 0x00
+#define OP_APPKEY_DELETE 0x8000
+#define OP_APPKEY_GET 0x8001
+#define OP_APPKEY_LIST 0x8002
+#define OP_APPKEY_STATUS 0x8003
+#define OP_APPKEY_UPDATE 0x01
+#define OP_DEV_COMP_GET 0x8008
+#define OP_DEV_COMP_STATUS 0x02
+#define OP_CONFIG_BEACON_GET 0x8009
+#define OP_CONFIG_BEACON_SET 0x800A
+#define OP_CONFIG_BEACON_STATUS 0x800B
+#define OP_CONFIG_DEFAULT_TTL_GET 0x800C
+#define OP_CONFIG_DEFAULT_TTL_SET 0x800D
+#define OP_CONFIG_DEFAULT_TTL_STATUS 0x800E
+#define OP_CONFIG_FRIEND_GET 0x800F
+#define OP_CONFIG_FRIEND_SET 0x8010
+#define OP_CONFIG_FRIEND_STATUS 0x8011
+#define OP_CONFIG_PROXY_GET 0x8012
+#define OP_CONFIG_PROXY_SET 0x8013
+#define OP_CONFIG_PROXY_STATUS 0x8014
+#define OP_CONFIG_KEY_REFRESH_PHASE_GET 0x8015
+#define OP_CONFIG_KEY_REFRESH_PHASE_SET 0x8016
+#define OP_CONFIG_KEY_REFRESH_PHASE_STATUS 0x8017
+#define OP_CONFIG_MODEL_PUB_GET 0x8018
+#define OP_CONFIG_MODEL_PUB_SET 0x03
+#define OP_CONFIG_MODEL_PUB_STATUS 0x8019
+#define OP_CONFIG_MODEL_PUB_VIRT_SET 0x801A
+#define OP_CONFIG_MODEL_SUB_ADD 0x801B
+#define OP_CONFIG_MODEL_SUB_DELETE 0x801C
+#define OP_CONFIG_MODEL_SUB_DELETE_ALL 0x801D
+#define OP_CONFIG_MODEL_SUB_OVERWRITE 0x801E
+#define OP_CONFIG_MODEL_SUB_STATUS 0x801F
+#define OP_CONFIG_MODEL_SUB_VIRT_ADD 0x8020
+#define OP_CONFIG_MODEL_SUB_VIRT_DELETE 0x8021
+#define OP_CONFIG_MODEL_SUB_VIRT_OVERWRITE 0x8022
+#define OP_CONFIG_NETWORK_TRANSMIT_GET 0x8023
+#define OP_CONFIG_NETWORK_TRANSMIT_SET 0x8024
+#define OP_CONFIG_NETWORK_TRANSMIT_STATUS 0x8025
+#define OP_CONFIG_RELAY_GET 0x8026
+#define OP_CONFIG_RELAY_SET 0x8027
+#define OP_CONFIG_RELAY_STATUS 0x8028
+#define OP_CONFIG_MODEL_SUB_GET 0x8029
+#define OP_CONFIG_MODEL_SUB_LIST 0x802A
+#define OP_CONFIG_VEND_MODEL_SUB_GET 0x802B
+#define OP_CONFIG_VEND_MODEL_SUB_LIST 0x802C
+#define OP_CONFIG_POLL_TIMEOUT_LIST 0x802D
+#define OP_CONFIG_POLL_TIMEOUT_STATUS 0x802E
+#define OP_CONFIG_HEARTBEAT_PUB_GET 0x8038
+#define OP_CONFIG_HEARTBEAT_PUB_SET 0x8039
+#define OP_CONFIG_HEARTBEAT_PUB_STATUS 0x06
+#define OP_CONFIG_HEARTBEAT_SUB_GET 0x803A
+#define OP_CONFIG_HEARTBEAT_SUB_SET 0x803B
+#define OP_CONFIG_HEARTBEAT_SUB_STATUS 0x803C
+#define OP_MODEL_APP_BIND 0x803D
+#define OP_MODEL_APP_STATUS 0x803E
+#define OP_MODEL_APP_UNBIND 0x803F
+#define OP_NETKEY_ADD 0x8040
+#define OP_NETKEY_DELETE 0x8041
+#define OP_NETKEY_GET 0x8042
+#define OP_NETKEY_LIST 0x8043
+#define OP_NETKEY_STATUS 0x8044
+#define OP_NETKEY_UPDATE 0x8045
+#define OP_NODE_IDENTITY_GET 0x8046
+#define OP_NODE_IDENTITY_SET 0x8047
+#define OP_NODE_IDENTITY_STATUS 0x8048
+#define OP_NODE_RESET 0x8049
+#define OP_NODE_RESET_STATUS 0x804A
+#define OP_MODEL_APP_GET 0x804B
+#define OP_MODEL_APP_LIST 0x804C
+#define OP_VEND_MODEL_APP_GET 0x804D
+#define OP_VEND_MODEL_APP_LIST 0x804E
+
+bool config_server_init(void);
+bool config_client_init(void);
+void config_client_get_composition(uint32_t dst);
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2017 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>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <sys/uio.h>
+#include <wordexp.h>
+
+#include <glib.h>
+
+#include "src/shared/util.h"
+#include "src/shared/shell.h"
+#include "mesh/mesh-net.h"
+#include "mesh/keys.h"
+#include "mesh/net.h"
+#include "mesh/node.h"
+#include "mesh/prov-db.h"
+#include "mesh/util.h"
+#include "mesh/config-model.h"
+
+static bool server_msg_recvd(uint16_t src, uint8_t *data,
+ uint16_t len, void *user_data)
+{
+ uint32_t opcode;
+ uint8_t msg[32];
+ struct mesh_node *node;
+ uint16_t primary;
+ uint32_t mod_id;
+ uint16_t ele_addr;
+ uint8_t ele_idx;
+ struct mesh_publication pub;
+ int m, n;
+
+ if (mesh_opcode_get(data, len, &opcode, &n)) {
+ len -= n;
+ data += n;
+ } else
+ return false;
+
+ node = node_get_local_node();
+
+ if (!node)
+ return true;
+
+ n = 0;
+
+ switch (opcode & ~OP_UNRELIABLE) {
+ default:
+ return false;
+
+ case OP_CONFIG_DEFAULT_TTL_SET:
+ if (len != 1 || data[0] > TTL_MASK || data[0] == 1)
+ return true;
+
+ if (data[0] <= TTL_MASK) {
+ node_set_default_ttl(node, data[0]);
+ prov_db_node_set_ttl(node, data[0]);
+ }
+
+ /* Fall Through */
+
+ case OP_CONFIG_DEFAULT_TTL_GET:
+ n = mesh_opcode_set(OP_CONFIG_DEFAULT_TTL_STATUS, msg);
+ msg[n++] = node_get_default_ttl(node);
+ break;
+
+ case OP_CONFIG_MODEL_PUB_SET:
+
+ if (len != 11 && len != 13)
+ return true;
+
+ bt_shell_printf("Set publication\n");
+
+ ele_addr = get_le16(data);
+ mod_id = get_le16(data + 9);
+ if (len == 14)
+ mod_id = (mod_id << 16) | get_le16(data + 11);
+ else
+ mod_id |= 0xffff0000;
+
+ pub.u.addr16 = get_le16(data + 2);
+ pub.app_idx = get_le16(data + 4);
+ pub.ttl = data[6];
+ pub.period = data[7];
+ m = (data[7] & 0x3f);
+ switch (data[7] >> 6) {
+ case 0:
+ bt_shell_printf("Period: %d ms\n", m * 100);
+ break;
+ case 2:
+ m *= 10;
+ /* fall through */
+ case 1:
+ bt_shell_printf("Period: %d sec\n", m);
+ break;
+ case 3:
+ bt_shell_printf("Period: %d min\n", m * 10);
+ break;
+ }
+
+ pub.retransmit = data[8];
+ bt_shell_printf("Retransmit count: %d\n", data[8] >> 5);
+ bt_shell_printf("Retransmit Interval Steps: %d\n", data[8] & 0x1f);
+
+ ele_idx = ele_addr - node_get_primary(node);
+
+ if (node_model_pub_set(node, ele_idx, mod_id, &pub)) {
+ prov_db_node_set_model_pub(node, ele_idx, mod_id,
+ node_model_pub_get(node, ele_idx, mod_id));
+ }
+ break;
+ }
+
+ if (!n)
+ return true;
+
+ primary = node_get_primary(node);
+ if (src != primary)
+ net_access_layer_send(node_get_default_ttl(node), primary,
+ src, APP_IDX_DEV, msg, n);
+ else
+ node_local_data_handler(primary, src, node_get_iv_index(node),
+ node_get_sequence_number(node),
+ APP_IDX_DEV, msg, n);
+ return true;
+}
+
+
+static struct mesh_model_ops server_cbs = {
+ server_msg_recvd,
+ NULL,
+ NULL,
+ NULL
+};
+
+bool config_server_init(void)
+{
+ if (!node_local_model_register(PRIMARY_ELEMENT_IDX,
+ CONFIG_SERVER_MODEL_ID,
+ &server_cbs, NULL))
+ return false;
+
+ return true;
+}
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2017 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 <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/socket.h>
+
+#include <linux/if_alg.h>
+
+#include <glib.h>
+
+#ifndef SOL_ALG
+#define SOL_ALG 279
+#endif
+
+#ifndef ALG_SET_AEAD_AUTHSIZE
+#define ALG_SET_AEAD_AUTHSIZE 5
+#endif
+
+#include "src/shared/util.h"
+#include "mesh/mesh-net.h"
+#include "mesh/crypto.h"
+
+static int alg_new(int fd, const void *keyval, socklen_t keylen,
+ size_t mic_size)
+{
+ if (setsockopt(fd, SOL_ALG, ALG_SET_KEY, keyval, keylen) < 0) {
+ g_printerr("key");
+ return -1;
+ }
+
+ if (mic_size &&
+ setsockopt(fd, SOL_ALG,
+ ALG_SET_AEAD_AUTHSIZE, NULL, mic_size) < 0) {
+ g_printerr("taglen");
+ return -1;
+ }
+
+ /* FIXME: This should use accept4() with SOCK_CLOEXEC */
+ return accept(fd, NULL, 0);
+}
+
+static bool alg_encrypt(int fd, const void *inbuf, size_t inlen,
+ void *outbuf, size_t outlen)
+{
+ __u32 alg_op = ALG_OP_ENCRYPT;
+ char cbuf[CMSG_SPACE(sizeof(alg_op))];
+ struct cmsghdr *cmsg;
+ struct msghdr msg;
+ struct iovec iov;
+ ssize_t len;
+
+ memset(cbuf, 0, sizeof(cbuf));
+ memset(&msg, 0, sizeof(msg));
+
+ msg.msg_control = cbuf;
+ msg.msg_controllen = sizeof(cbuf);
+
+ cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_level = SOL_ALG;
+ cmsg->cmsg_type = ALG_SET_OP;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(alg_op));
+ memcpy(CMSG_DATA(cmsg), &alg_op, sizeof(alg_op));
+
+ iov.iov_base = (void *) inbuf;
+ iov.iov_len = inlen;
+
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ len = sendmsg(fd, &msg, 0);
+ if (len < 0)
+ return false;
+
+ len = read(fd, outbuf, outlen);
+ if (len < 0)
+ return false;
+
+ return true;
+}
+
+static int aes_ecb_setup(const uint8_t key[16])
+{
+ struct sockaddr_alg salg;
+ int fd, nfd;
+
+ fd = socket(PF_ALG, SOCK_SEQPACKET | SOCK_CLOEXEC, 0);
+ if (fd < 0)
+ return -1;
+
+ memset(&salg, 0, sizeof(salg));
+ salg.salg_family = AF_ALG;
+ strcpy((char *) salg.salg_type, "skcipher");
+ strcpy((char *) salg.salg_name, "ecb(aes)");
+
+ if (bind(fd, (struct sockaddr *) &salg, sizeof(salg)) < 0) {
+ close(fd);
+ return -1;
+ }
+
+ nfd = alg_new(fd, key, 16, 0);
+
+ close(fd);
+
+ return nfd;
+}
+
+static bool aes_ecb(int fd, const uint8_t plaintext[16], uint8_t encrypted[16])
+{
+ return alg_encrypt(fd, plaintext, 16, encrypted, 16);
+}
+
+static void aes_ecb_destroy(int fd)
+{
+ close(fd);
+}
+
+static bool aes_ecb_one(const uint8_t key[16],
+ const uint8_t plaintext[16], uint8_t encrypted[16])
+{
+ bool result;
+ int fd;
+
+ fd = aes_ecb_setup(key);
+ if (fd < 0)
+ return false;
+
+ result = aes_ecb(fd, plaintext, encrypted);
+
+ aes_ecb_destroy(fd);
+
+ return result;
+}
+
+/* Maximum message length that can be passed to aes_cmac */
+#define CMAC_MSG_MAX (64 + 64 + 17)
+
+static int aes_cmac_setup(const uint8_t key[16])
+{
+ struct sockaddr_alg salg;
+ int fd, nfd;
+
+ fd = socket(PF_ALG, SOCK_SEQPACKET | SOCK_CLOEXEC, 0);
+ if (fd < 0)
+ return -1;
+
+ memset(&salg, 0, sizeof(salg));
+ salg.salg_family = AF_ALG;
+ strcpy((char *) salg.salg_type, "hash");
+ strcpy((char *) salg.salg_name, "cmac(aes)");
+
+ if (bind(fd, (struct sockaddr *) &salg, sizeof(salg)) < 0) {
+ close(fd);
+ return -1;
+ }
+
+ nfd = alg_new(fd, key, 16, 0);
+
+ close(fd);
+
+ return nfd;
+}
+
+static bool aes_cmac(int fd, const uint8_t *msg,
+ size_t msg_len, uint8_t res[16])
+{
+ ssize_t len;
+
+ if (msg_len > CMAC_MSG_MAX)
+ return false;
+
+ len = send(fd, msg, msg_len, 0);
+ if (len < 0)
+ return false;
+
+ len = read(fd, res, 16);
+ if (len < 0)
+ return false;
+
+ return true;
+}
+
+static void aes_cmac_destroy(int fd)
+{
+ close(fd);
+}
+
+static int aes_cmac_N_start(const uint8_t N[16])
+{
+ int fd;
+
+ fd = aes_cmac_setup(N);
+ return fd;
+}
+
+static bool aes_cmac_one(const uint8_t key[16], const void *msg,
+ size_t msg_len, uint8_t res[16])
+{
+ bool result;
+ int fd;
+
+ fd = aes_cmac_setup(key);
+ if (fd < 0)
+ return false;
+
+ result = aes_cmac(fd, msg, msg_len, res);
+
+ aes_cmac_destroy(fd);
+
+ return result;
+}
+
+bool mesh_crypto_aes_cmac(const uint8_t key[16], const uint8_t *msg,
+ size_t msg_len, uint8_t res[16])
+{
+ return aes_cmac_one(key, msg, msg_len, res);
+}
+
+bool mesh_crypto_aes_ccm_encrypt(const uint8_t nonce[13], const uint8_t key[16],
+ const uint8_t *aad, uint16_t aad_len,
+ const uint8_t *msg, uint16_t msg_len,
+ uint8_t *out_msg, void *out_mic,
+ size_t mic_size)
+{
+ uint8_t pmsg[16], cmic[16], cmsg[16];
+ uint8_t mic[16], Xn[16];
+ uint16_t blk_cnt, last_blk;
+ bool result;
+ size_t i, j;
+ int fd;
+
+ if (aad_len >= 0xff00) {
+ g_printerr("Unsupported AAD size");
+ return false;
+ }
+
+ fd = aes_ecb_setup(key);
+ if (fd < 0)
+ return false;
+
+ /* C_mic = e(AppKey, 0x01 || nonce || 0x0000) */
+ pmsg[0] = 0x01;
+ memcpy(pmsg + 1, nonce, 13);
+ put_be16(0x0000, pmsg + 14);
+
+ result = aes_ecb(fd, pmsg, cmic);
+ if (!result)
+ goto done;
+
+ /* X_0 = e(AppKey, 0x09 || nonce || length) */
+ if (mic_size == sizeof(uint64_t))
+ pmsg[0] = 0x19 | (aad_len ? 0x40 : 0x00);
+ else
+ pmsg[0] = 0x09 | (aad_len ? 0x40 : 0x00);
+
+ memcpy(pmsg + 1, nonce, 13);
+ put_be16(msg_len, pmsg + 14);
+
+ result = aes_ecb(fd, pmsg, Xn);
+ if (!result)
+ goto done;
+
+ /* If AAD is being used to authenticate, include it here */
+ if (aad_len) {
+ put_be16(aad_len, pmsg);
+
+ for (i = 0; i < sizeof(uint16_t); i++)
+ pmsg[i] = Xn[i] ^ pmsg[i];
+
+ j = 0;
+ aad_len += sizeof(uint16_t);
+ while (aad_len > 16) {
+ do {
+ pmsg[i] = Xn[i] ^ aad[j];
+ i++, j++;
+ } while (i < 16);
+
+ aad_len -= 16;
+ i = 0;
+
+ result = aes_ecb(fd, pmsg, Xn);
+ if (!result)
+ goto done;
+ }
+
+ for (i = 0; i < aad_len; i++, j++)
+ pmsg[i] = Xn[i] ^ aad[j];
+
+ for (i = aad_len; i < 16; i++)
+ pmsg[i] = Xn[i];
+
+ result = aes_ecb(fd, pmsg, Xn);
+ if (!result)
+ goto done;
+ }
+
+ last_blk = msg_len % 16;
+ blk_cnt = (msg_len + 15) / 16;
+ if (!last_blk)
+ last_blk = 16;
+
+ for (j = 0; j < blk_cnt; j++) {
+ if (j + 1 == blk_cnt) {
+ /* X_1 = e(AppKey, X_0 ^ Payload[0-15]) */
+ for (i = 0; i < last_blk; i++)
+ pmsg[i] = Xn[i] ^ msg[(j * 16) + i];
+ for (i = last_blk; i < 16; i++)
+ pmsg[i] = Xn[i] ^ 0x00;
+
+ result = aes_ecb(fd, pmsg, Xn);
+ if (!result)
+ goto done;
+
+ /* MIC = C_mic ^ X_1 */
+ for (i = 0; i < sizeof(mic); i++)
+ mic[i] = cmic[i] ^ Xn[i];
+
+ /* C_1 = e(AppKey, 0x01 || nonce || 0x0001) */
+ pmsg[0] = 0x01;
+ memcpy(pmsg + 1, nonce, 13);
+ put_be16(j + 1, pmsg + 14);
+
+ result = aes_ecb(fd, pmsg, cmsg);
+ if (!result)
+ goto done;
+
+ if (out_msg) {
+ /* Encrypted = Payload[0-15] ^ C_1 */
+ for (i = 0; i < last_blk; i++)
+ out_msg[(j * 16) + i] =
+ msg[(j * 16) + i] ^ cmsg[i];
+
+ }
+ } else {
+ /* X_1 = e(AppKey, X_0 ^ Payload[0-15]) */
+ for (i = 0; i < 16; i++)
+ pmsg[i] = Xn[i] ^ msg[(j * 16) + i];
+
+ result = aes_ecb(fd, pmsg, Xn);
+ if (!result)
+ goto done;
+
+ /* C_1 = e(AppKey, 0x01 || nonce || 0x0001) */
+ pmsg[0] = 0x01;
+ memcpy(pmsg + 1, nonce, 13);
+ put_be16(j + 1, pmsg + 14);
+
+ result = aes_ecb(fd, pmsg, cmsg);
+ if (!result)
+ goto done;
+
+ if (out_msg) {
+ /* Encrypted = Payload[0-15] ^ C_N */
+ for (i = 0; i < 16; i++)
+ out_msg[(j * 16) + i] =
+ msg[(j * 16) + i] ^ cmsg[i];
+ }
+
+ }
+ }
+
+ if (out_msg)
+ memcpy(out_msg + msg_len, mic, mic_size);
+
+ if (out_mic) {
+ switch (mic_size) {
+ case sizeof(uint32_t):
+ *(uint32_t *)out_mic = get_be32(mic);
+ break;
+ case sizeof(uint64_t):
+ *(uint64_t *)out_mic = get_be64(mic);
+ break;
+ default:
+ g_printerr("Unsupported MIC size");
+ }
+ }
+
+done:
+ aes_ecb_destroy(fd);
+
+ return result;
+}
+
+bool mesh_crypto_aes_ccm_decrypt(const uint8_t nonce[13], const uint8_t key[16],
+ const uint8_t *aad, uint16_t aad_len,
+ const uint8_t *enc_msg, uint16_t enc_msg_len,
+ uint8_t *out_msg, void *out_mic,
+ size_t mic_size)
+{
+ uint8_t msg[16], pmsg[16], cmic[16], cmsg[16], Xn[16];
+ uint8_t mic[16];
+ uint16_t msg_len = enc_msg_len - mic_size;
+ uint16_t last_blk, blk_cnt;
+ bool result;
+ size_t i, j;
+ int fd;
+
+ if (enc_msg_len < 5 || aad_len >= 0xff00)
+ return false;
+
+ fd = aes_ecb_setup(key);
+ if (fd < 0)
+ return false;
+
+ /* C_mic = e(AppKey, 0x01 || nonce || 0x0000) */
+ pmsg[0] = 0x01;
+ memcpy(pmsg + 1, nonce, 13);
+ put_be16(0x0000, pmsg + 14);
+
+ result = aes_ecb(fd, pmsg, cmic);
+ if (!result)
+ goto done;
+
+ /* X_0 = e(AppKey, 0x09 || nonce || length) */
+ if (mic_size == sizeof(uint64_t))
+ pmsg[0] = 0x19 | (aad_len ? 0x40 : 0x00);
+ else
+ pmsg[0] = 0x09 | (aad_len ? 0x40 : 0x00);
+
+ memcpy(pmsg + 1, nonce, 13);
+ put_be16(msg_len, pmsg + 14);
+
+ result = aes_ecb(fd, pmsg, Xn);
+ if (!result)
+ goto done;
+
+ /* If AAD is being used to authenticate, include it here */
+ if (aad_len) {
+ put_be16(aad_len, pmsg);
+
+ for (i = 0; i < sizeof(uint16_t); i++)
+ pmsg[i] = Xn[i] ^ pmsg[i];
+
+ j = 0;
+ aad_len += sizeof(uint16_t);
+ while (aad_len > 16) {
+ do {
+ pmsg[i] = Xn[i] ^ aad[j];
+ i++, j++;
+ } while (i < 16);
+
+ aad_len -= 16;
+ i = 0;
+
+ result = aes_ecb(fd, pmsg, Xn);
+ if (!result)
+ goto done;
+ }
+
+ for (i = 0; i < aad_len; i++, j++)
+ pmsg[i] = Xn[i] ^ aad[j];
+
+ for (i = aad_len; i < 16; i++)
+ pmsg[i] = Xn[i];
+
+ result = aes_ecb(fd, pmsg, Xn);
+ if (!result)
+ goto done;
+ }
+
+ last_blk = msg_len % 16;
+ blk_cnt = (msg_len + 15) / 16;
+ if (!last_blk)
+ last_blk = 16;
+
+ for (j = 0; j < blk_cnt; j++) {
+ if (j + 1 == blk_cnt) {
+ /* C_1 = e(AppKey, 0x01 || nonce || 0x0001) */
+ pmsg[0] = 0x01;
+ memcpy(pmsg + 1, nonce, 13);
+ put_be16(j + 1, pmsg + 14);
+
+ result = aes_ecb(fd, pmsg, cmsg);
+ if (!result)
+ goto done;
+
+ /* Encrypted = Payload[0-15] ^ C_1 */
+ for (i = 0; i < last_blk; i++)
+ msg[i] = enc_msg[(j * 16) + i] ^ cmsg[i];
+
+ if (out_msg)
+ memcpy(out_msg + (j * 16), msg, last_blk);
+
+ /* X_1 = e(AppKey, X_0 ^ Payload[0-15]) */
+ for (i = 0; i < last_blk; i++)
+ pmsg[i] = Xn[i] ^ msg[i];
+ for (i = last_blk; i < 16; i++)
+ pmsg[i] = Xn[i] ^ 0x00;
+
+ result = aes_ecb(fd, pmsg, Xn);
+ if (!result)
+ goto done;
+
+ /* MIC = C_mic ^ X_1 */
+ for (i = 0; i < sizeof(mic); i++)
+ mic[i] = cmic[i] ^ Xn[i];
+ } else {
+ /* C_1 = e(AppKey, 0x01 || nonce || 0x0001) */
+ pmsg[0] = 0x01;
+ memcpy(pmsg + 1, nonce, 13);
+ put_be16(j + 1, pmsg + 14);
+
+ result = aes_ecb(fd, pmsg, cmsg);
+ if (!result)
+ goto done;
+
+ /* Encrypted = Payload[0-15] ^ C_1 */
+ for (i = 0; i < 16; i++)
+ msg[i] = enc_msg[(j * 16) + i] ^ cmsg[i];
+
+ if (out_msg)
+ memcpy(out_msg + (j * 16), msg, 16);
+
+ /* X_1 = e(AppKey, X_0 ^ Payload[0-15]) */
+ for (i = 0; i < 16; i++)
+ pmsg[i] = Xn[i] ^ msg[i];
+
+ result = aes_ecb(fd, pmsg, Xn);
+ if (!result)
+ goto done;
+ }
+ }
+
+ switch (mic_size) {
+ case sizeof(uint32_t):
+ if (out_mic)
+ *(uint32_t *)out_mic = get_be32(mic);
+ else if (get_be32(enc_msg + enc_msg_len - mic_size) !=
+ get_be32(mic))
+ result = false;
+ break;
+
+ case sizeof(uint64_t):
+ if (out_mic)
+ *(uint64_t *)out_mic = get_be64(mic);
+ else if (get_be64(enc_msg + enc_msg_len - mic_size) !=
+ get_be64(mic))
+ result = false;
+ break;
+
+ default:
+ g_printerr("Unsupported MIC size");
+ result = false;
+ }
+
+done:
+ aes_ecb_destroy(fd);
+
+ return result;
+}
+
+bool mesh_crypto_k1(const uint8_t ikm[16], const uint8_t salt[16],
+ const void *info, size_t info_len, uint8_t okm[16])
+{
+ uint8_t res[16];
+
+ if (!aes_cmac_one(salt, ikm, 16, res))
+ return false;
+
+ return aes_cmac_one(res, info, info_len, okm);
+}
+
+bool mesh_crypto_k2(const uint8_t n[16], const uint8_t *p, size_t p_len,
+ uint8_t net_id[1],
+ uint8_t enc_key[16],
+ uint8_t priv_key[16])
+{
+ int fd;
+ uint8_t output[16];
+ uint8_t t[16];
+ uint8_t *stage;
+ bool success = false;
+
+ stage = g_malloc(sizeof(output) + p_len + 1);
+ if (stage == NULL)
+ return false;
+
+ if (!mesh_crypto_s1("smk2", 4, stage))
+ goto fail;
+
+ if (!aes_cmac_one(stage, n, 16, t))
+ goto fail;
+
+ fd = aes_cmac_N_start(t);
+ if (fd < 0)
+ goto fail;
+
+ memcpy(stage, p, p_len);
+ stage[p_len] = 1;
+
+ if(!aes_cmac(fd, stage, p_len + 1, output))
+ goto done;
+
+ net_id[0] = output[15] & 0x7f;
+
+ memcpy(stage, output, 16);
+ memcpy(stage + 16, p, p_len);
+ stage[p_len + 16] = 2;
+
+ if(!aes_cmac(fd, stage, p_len + 16 + 1, output))
+ goto done;
+
+ memcpy(enc_key, output, 16);
+
+ memcpy(stage, output, 16);
+ memcpy(stage + 16, p, p_len);
+ stage[p_len + 16] = 3;
+
+ if(!aes_cmac(fd, stage, p_len + 16 + 1, output))
+ goto done;
+
+ memcpy(priv_key, output, 16);
+ success = true;
+
+done:
+ aes_cmac_destroy(fd);
+fail:
+ g_free(stage);
+
+ return success;
+}
+
+static bool crypto_128(const uint8_t n[16], const char *s, uint8_t out128[16])
+{
+ uint8_t id128[] = { 'i', 'd', '1', '2', '8', 0x01 };
+ uint8_t salt[16];
+
+ if (!mesh_crypto_s1(s, 4, salt))
+ return false;
+
+ return mesh_crypto_k1(n, salt, id128, sizeof(id128), out128);
+}
+
+bool mesh_crypto_nkik(const uint8_t n[16], uint8_t identity_key[16])
+{
+ return crypto_128(n, "nkik", identity_key);
+}
+
+static bool identity_calc(const uint8_t net_key[16], uint16_t addr,
+ bool check, uint8_t id[16])
+{
+ uint8_t id_key[16];
+ uint8_t tmp[16];
+
+ if (!mesh_crypto_nkik(net_key, id_key))
+ return false;
+
+ memset(tmp, 0, sizeof(tmp));
+ put_be16(addr, tmp + 14);
+
+ if (check) {
+ memcpy(tmp + 6, id + 8, 8);
+ } else {
+ mesh_get_random_bytes(tmp + 6, 8);
+ memcpy(id + 8, tmp + 6, 8);
+ }
+
+ if (!aes_ecb_one(id_key, tmp, tmp))
+ return false;
+
+ if (check)
+ return (memcmp(id, tmp + 8, 8) == 0);
+
+ memcpy(id, tmp + 8, 8);
+ return true;
+}
+
+bool mesh_crypto_identity(const uint8_t net_key[16], uint16_t addr,
+ uint8_t id[16])
+{
+ return identity_calc(net_key, addr, false, id);
+}
+
+bool mesh_crypto_identity_check(const uint8_t net_key[16], uint16_t addr,
+ uint8_t id[16])
+{
+ return identity_calc(net_key, addr, true, id);
+}
+
+bool mesh_crypto_nkbk(const uint8_t n[16], uint8_t beacon_key[16])
+{
+ return crypto_128(n, "nkbk", beacon_key);
+}
+
+bool mesh_crypto_k3(const uint8_t n[16], uint8_t out64[8])
+{
+ uint8_t tmp[16];
+ uint8_t t[16];
+ uint8_t id64[] = { 'i', 'd', '6', '4', 0x01 };
+
+ if (!mesh_crypto_s1("smk3", 4, tmp))
+ return false;
+
+ if (!aes_cmac_one(tmp, n, 16, t))
+ return false;
+
+ if (!aes_cmac_one(t, id64, sizeof(id64), tmp))
+ return false;
+
+ memcpy(out64, tmp + 8, 8);
+
+ return true;
+}
+
+bool mesh_crypto_k4(const uint8_t a[16], uint8_t out6[1])
+{
+ uint8_t tmp[16];
+ uint8_t t[16];
+ uint8_t id6[] = { 'i', 'd', '6', 0x01 };
+
+ if (!mesh_crypto_s1("smk4", 4, tmp))
+ return false;
+
+ if (!aes_cmac_one(tmp, a, 16, t))
+ return false;
+
+ if (!aes_cmac_one(t, id6, sizeof(id6), tmp))
+ return false;
+
+ out6[0] = tmp[15] & 0x3f;
+ return true;
+}
+
+bool mesh_crypto_beacon_cmac(const uint8_t encryption_key[16],
+ const uint8_t network_id[8],
+ uint32_t iv_index, bool kr, bool iu,
+ uint64_t *cmac)
+{
+ uint8_t msg[13], tmp[16];
+
+ if (!cmac)
+ return false;
+
+ msg[0] = kr ? 0x01 : 0x00;
+ msg[0] |= iu ? 0x02 : 0x00;
+ memcpy(msg + 1, network_id, 8);
+ put_be32(iv_index, msg + 9);
+
+ if (!aes_cmac_one(encryption_key, msg, 13, tmp))
+ return false;
+
+ *cmac = get_be64(tmp);
+
+ return true;
+}
+
+bool mesh_crypto_network_nonce(bool ctl, uint8_t ttl, uint32_t seq,
+ uint16_t src, uint32_t iv_index,
+ uint8_t nonce[13])
+{
+ nonce[0] = 0;
+ nonce[1] = (ttl & TTL_MASK) | (ctl ? CTL : 0x00);
+ nonce[2] = (seq >> 16) & 0xff;
+ nonce[3] = (seq >> 8) & 0xff;
+ nonce[4] = seq & 0xff;
+
+ /* SRC */
+ put_be16(src, nonce + 5);
+
+ put_be16(0, nonce + 7);
+
+ /* IV Index */
+ put_be32(iv_index, nonce + 9);
+
+ return true;
+}
+
+bool mesh_crypto_network_encrypt(bool ctl, uint8_t ttl,
+ uint32_t seq, uint16_t src,
+ uint32_t iv_index,
+ const uint8_t net_key[16],
+ const uint8_t *enc_msg, uint8_t enc_msg_len,
+ uint8_t *out, void *net_mic)
+{
+ uint8_t nonce[13];
+
+ if (!mesh_crypto_network_nonce(ctl, ttl, seq, src, iv_index, nonce))
+ return false;
+
+ return mesh_crypto_aes_ccm_encrypt(nonce, net_key,
+ NULL, 0, enc_msg,
+ enc_msg_len, out,
+ net_mic,
+ ctl ? sizeof(uint64_t) : sizeof(uint32_t));
+}
+
+bool mesh_crypto_network_decrypt(bool ctl, uint8_t ttl,
+ uint32_t seq, uint16_t src,
+ uint32_t iv_index,
+ const uint8_t net_key[16],
+ const uint8_t *enc_msg, uint8_t enc_msg_len,
+ uint8_t *out, void *net_mic, size_t mic_size)
+{
+ uint8_t nonce[13];
+
+ if (!mesh_crypto_network_nonce(ctl, ttl, seq, src, iv_index, nonce))
+ return false;
+
+ return mesh_crypto_aes_ccm_decrypt(nonce, net_key, NULL, 0,
+ enc_msg, enc_msg_len, out,
+ net_mic, mic_size);
+}
+
+bool mesh_crypto_application_nonce(uint32_t seq, uint16_t src,
+ uint16_t dst, uint32_t iv_index,
+ bool aszmic, uint8_t nonce[13])
+{
+ nonce[0] = 0x01;
+ nonce[1] = aszmic ? 0x80 : 0x00;
+ nonce[2] = (seq & 0x00ff0000) >> 16;
+ nonce[3] = (seq & 0x0000ff00) >> 8;
+ nonce[4] = (seq & 0x000000ff);
+ nonce[5] = (src & 0xff00) >> 8;
+ nonce[6] = (src & 0x00ff);
+ nonce[7] = (dst & 0xff00) >> 8;
+ nonce[8] = (dst & 0x00ff);
+ put_be32(iv_index, nonce + 9);
+
+ return true;
+}
+
+bool mesh_crypto_device_nonce(uint32_t seq, uint16_t src,
+ uint16_t dst, uint32_t iv_index,
+ bool aszmic, uint8_t nonce[13])
+{
+ nonce[0] = 0x02;
+ nonce[1] = aszmic ? 0x80 : 0x00;
+ nonce[2] = (seq & 0x00ff0000) >> 16;
+ nonce[3] = (seq & 0x0000ff00) >> 8;
+ nonce[4] = (seq & 0x000000ff);
+ nonce[5] = (src & 0xff00) >> 8;
+ nonce[6] = (src & 0x00ff);
+ nonce[7] = (dst & 0xff00) >> 8;
+ nonce[8] = (dst & 0x00ff);
+ put_be32(iv_index, nonce + 9);
+
+ return true;
+}
+
+bool mesh_crypto_application_encrypt(uint8_t key_id, uint32_t seq, uint16_t src,
+ uint16_t dst, uint32_t iv_index,
+ const uint8_t app_key[16],
+ const uint8_t *aad, uint8_t aad_len,
+ const uint8_t *msg, uint8_t msg_len,
+ uint8_t *out, void *app_mic,
+ size_t mic_size)
+{
+ uint8_t nonce[13];
+ bool aszmic = (mic_size == sizeof(uint64_t)) ? true : false;
+
+ if (!key_id && !mesh_crypto_device_nonce(seq, src, dst,
+ iv_index, aszmic, nonce))
+ return false;
+
+ if (key_id && !mesh_crypto_application_nonce(seq, src, dst,
+ iv_index, aszmic, nonce))
+ return false;
+
+ return mesh_crypto_aes_ccm_encrypt(nonce, app_key, aad, aad_len,
+ msg, msg_len,
+ out, app_mic, mic_size);
+}
+
+bool mesh_crypto_application_decrypt(uint8_t key_id, uint32_t seq, uint16_t src,
+ uint16_t dst, uint32_t iv_index,
+ const uint8_t app_key[16],
+ const uint8_t *aad, uint8_t aad_len,
+ const uint8_t *enc_msg, uint8_t enc_msg_len,
+ uint8_t *out, void *app_mic, size_t mic_size)
+{
+ uint8_t nonce[13];
+ bool aszmic = (mic_size == sizeof(uint64_t)) ? true : false;
+
+ if (!key_id && !mesh_crypto_device_nonce(seq, src, dst,
+ iv_index, aszmic, nonce))
+ return false;
+
+ if (key_id && !mesh_crypto_application_nonce(seq, src, dst,
+ iv_index, aszmic, nonce))
+ return false;
+
+ return mesh_crypto_aes_ccm_decrypt(nonce, app_key,
+ aad, aad_len, enc_msg,
+ enc_msg_len, out,
+ app_mic, mic_size);
+}
+
+bool mesh_crypto_session_key(const uint8_t secret[32],
+ const uint8_t salt[16],
+ uint8_t session_key[16])
+{
+ const uint8_t prsk[4] = "prsk";
+
+ if (!aes_cmac_one(salt, secret, 32, session_key))
+ return false;
+
+ return aes_cmac_one(session_key, prsk, 4, session_key);
+}
+
+bool mesh_crypto_nonce(const uint8_t secret[32],
+ const uint8_t salt[16],
+ uint8_t nonce[13])
+{
+ const uint8_t prsn[4] = "prsn";
+ uint8_t tmp[16];
+ bool result;
+
+ if (!aes_cmac_one(salt, secret, 32, tmp))
+ return false;
+
+ result = aes_cmac_one(tmp, prsn, 4, tmp);
+
+ if (result)
+ memcpy(nonce, tmp + 3, 13);
+
+ return result;
+}
+
+bool mesh_crypto_s1(const void *info, size_t len, uint8_t salt[16])
+{
+ const uint8_t zero[16] = {0};
+
+ return aes_cmac_one(zero, info, len, salt);
+}
+
+bool mesh_crypto_prov_prov_salt(const uint8_t conf_salt[16],
+ const uint8_t prov_rand[16],
+ const uint8_t dev_rand[16],
+ uint8_t prov_salt[16])
+{
+ const uint8_t zero[16] = {0};
+ uint8_t tmp[16 * 3];
+
+ memcpy(tmp, conf_salt, 16);
+ memcpy(tmp + 16, prov_rand, 16);
+ memcpy(tmp + 32, dev_rand, 16);
+
+ return aes_cmac_one(zero, tmp, sizeof(tmp), prov_salt);
+}
+
+bool mesh_crypto_prov_conf_key(const uint8_t secret[32],
+ const uint8_t salt[16],
+ uint8_t conf_key[16])
+{
+ const uint8_t prck[4] = "prck";
+
+ if (!aes_cmac_one(salt, secret, 32, conf_key))
+ return false;
+
+ return aes_cmac_one(conf_key, prck, 4, conf_key);
+}
+
+bool mesh_crypto_device_key(const uint8_t secret[32],
+ const uint8_t salt[16],
+ uint8_t device_key[16])
+{
+ const uint8_t prdk[4] = "prdk";
+
+ if (!aes_cmac_one(salt, secret, 32, device_key))
+ return false;
+
+ return aes_cmac_one(device_key, prdk, 4, device_key);
+}
+
+bool mesh_crypto_virtual_addr(const uint8_t virtual_label[16],
+ uint16_t *addr)
+{
+ uint8_t tmp[16];
+
+ if (!mesh_crypto_s1("vtad", 4, tmp))
+ return false;
+
+ if (!addr || !aes_cmac_one(tmp, virtual_label, 16, tmp))
+ return false;
+
+ *addr = (get_be16(tmp + 14) & 0x3fff) | 0x8000;
+
+ return true;
+}
+
+bool mesh_crypto_packet_encode(uint8_t *packet, uint8_t packet_len,
+ const uint8_t network_key[16],
+ uint32_t iv_index,
+ const uint8_t privacy_key[16])
+{
+ uint8_t network_nonce[13] = { 0x00, 0x00 };
+ uint8_t privacy_counter[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, };
+ uint8_t tmp[16];
+ int i;
+
+ /* Detect Proxy packet by CTL == true && DST == 0x0000 */
+ if ((packet[1] & CTL) && get_be16(packet + 7) == 0)
+ network_nonce[0] = 0x03;
+ else
+ /* CTL + TTL */
+ network_nonce[1] = packet[1];
+
+ /* Seq Num */
+ network_nonce[2] = packet[2];
+ network_nonce[3] = packet[3];
+ network_nonce[4] = packet[4];
+
+ /* SRC */
+ network_nonce[5] = packet[5];
+ network_nonce[6] = packet[6];
+
+ /* DST not available */
+ network_nonce[7] = 0;
+ network_nonce[8] = 0;
+
+ /* IV Index */
+ put_be32(iv_index, network_nonce + 9);
+
+ /* Check for Long net-MIC */
+ if (packet[1] & CTL) {
+ if (!mesh_crypto_aes_ccm_encrypt(network_nonce, network_key,
+ NULL, 0,
+ packet + 7, packet_len - 7 - 8,
+ packet + 7, NULL, sizeof(uint64_t)))
+ return false;
+ } else {
+ if (!mesh_crypto_aes_ccm_encrypt(network_nonce, network_key,
+ NULL, 0,
+ packet + 7, packet_len - 7 - 4,
+ packet + 7, NULL, sizeof(uint32_t)))
+ return false;
+ }
+
+ put_be32(iv_index, privacy_counter + 5);
+ memcpy(privacy_counter + 9, packet + 7, 7);
+
+ if (!aes_ecb_one(privacy_key, privacy_counter, tmp))
+ return false;
+
+ for (i = 0; i < 6; i++)
+ packet[1 + i] ^= tmp[i];
+
+ return true;
+}
+
+bool mesh_crypto_packet_decode(const uint8_t *packet, uint8_t packet_len,
+ bool proxy, uint8_t *out, uint32_t iv_index,
+ const uint8_t network_key[16],
+ const uint8_t privacy_key[16])
+{
+ uint8_t privacy_counter[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, };
+ uint8_t network_nonce[13] = { 0x00, 0x00, };
+ uint8_t tmp[16];
+ uint16_t src;
+ int i;
+
+ if (packet_len < 14)
+ return false;
+
+ put_be32(iv_index, privacy_counter + 5);
+ memcpy(privacy_counter + 9, packet + 7, 7);
+
+ if (!aes_ecb_one(privacy_key, privacy_counter, tmp))
+ return false;
+
+ memcpy(out, packet, packet_len);
+ for (i = 0; i < 6; i++)
+ out[1 + i] ^= tmp[i];
+
+ src = get_be16(out + 5);
+
+ /* Pre-check SRC address for illegal values */
+ if (!src || src >= 0x8000)
+ return false;
+
+ /* Detect Proxy packet by CTL == true && proxy == true */
+ if ((out[1] & CTL) && proxy)
+ network_nonce[0] = 0x03;
+ else
+ /* CTL + TTL */
+ network_nonce[1] = out[1];
+
+ /* Seq Num */
+ network_nonce[2] = out[2];
+ network_nonce[3] = out[3];
+ network_nonce[4] = out[4];
+
+ /* SRC */
+ network_nonce[5] = out[5];
+ network_nonce[6] = out[6];
+
+ /* DST not available */
+ network_nonce[7] = 0;
+ network_nonce[8] = 0;
+
+ /* IV Index */
+ put_be32(iv_index, network_nonce + 9);
+
+ /* Check for Long MIC */
+ if (out[1] & CTL) {
+ uint64_t mic;
+
+ if (!mesh_crypto_aes_ccm_decrypt(network_nonce, network_key,
+ NULL, 0, packet + 7, packet_len - 7,
+ out + 7, &mic, sizeof(mic)))
+ return false;
+
+ mic ^= get_be64(out + packet_len - 8);
+ put_be64(mic, out + packet_len - 8);
+
+ if (mic)
+ return false;
+ } else {
+ uint32_t mic;
+
+ if (!mesh_crypto_aes_ccm_decrypt(network_nonce, network_key,
+ NULL, 0, packet + 7, packet_len - 7,
+ out + 7, &mic, sizeof(mic)))
+ return false;
+
+ mic ^= get_be32(out + packet_len - 4);
+ put_be32(mic, out + packet_len - 4);
+
+ if (mic)
+ return false;
+ }
+
+ return true;
+}
+
+bool mesh_get_random_bytes(void *buf, size_t num_bytes)
+{
+ ssize_t len;
+ int fd;
+
+ fd = open("/dev/urandom", O_RDONLY);
+ if (fd < 0)
+ return false;
+
+ len = read(fd, buf, num_bytes);
+
+ close(fd);
+
+ if (len < 0)
+ return false;
+
+ return true;
+}
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2017 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
+ *
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+
+bool mesh_crypto_aes_ccm_encrypt(const uint8_t nonce[13], const uint8_t key[16],
+ const uint8_t *aad, uint16_t aad_len,
+ const uint8_t *msg, uint16_t msg_len,
+ uint8_t *out_msg, void *out_mic,
+ size_t mic_size);
+bool mesh_crypto_aes_ccm_decrypt(const uint8_t nonce[13], const uint8_t key[16],
+ const uint8_t *aad, uint16_t aad_len,
+ const uint8_t *enc_msg, uint16_t enc_msg_len,
+ uint8_t *out_msg, void *out_mic,
+ size_t mic_size);
+bool mesh_crypto_nkik(const uint8_t network_key[16], uint8_t identity_key[16]);
+bool mesh_crypto_nkbk(const uint8_t network_key[16], uint8_t beacon_key[16]);
+bool mesh_crypto_identity(const uint8_t net_key[16], uint16_t addr,
+ uint8_t id[16]);
+bool mesh_crypto_identity_check(const uint8_t net_key[16], uint16_t addr,
+ uint8_t id[16]);
+bool mesh_crypto_beacon_cmac(const uint8_t encryption_key[16],
+ const uint8_t network_id[16],
+ uint32_t iv_index, bool kr, bool iu,
+ uint64_t *cmac);
+bool mesh_crypto_network_nonce(bool frnd, uint8_t ttl, uint32_t seq,
+ uint16_t src, uint32_t iv_index,
+ uint8_t nonce[13]);
+bool mesh_crypto_network_encrypt(bool ctl, uint8_t ttl,
+ uint32_t seq, uint16_t src,
+ uint32_t iv_index,
+ const uint8_t net_key[16],
+ const uint8_t *enc_msg, uint8_t enc_msg_len,
+ uint8_t *out, void *net_mic);
+bool mesh_crypto_network_decrypt(bool frnd, uint8_t ttl,
+ uint32_t seq, uint16_t src,
+ uint32_t iv_index,
+ const uint8_t net_key[16],
+ const uint8_t *enc_msg, uint8_t enc_msg_len,
+ uint8_t *out, void *net_mic, size_t mic_size);
+bool mesh_crypto_application_nonce(uint32_t seq, uint16_t src,
+ uint16_t dst, uint32_t iv_index,
+ bool aszmic, uint8_t nonce[13]);
+bool mesh_crypto_device_nonce(uint32_t seq, uint16_t src,
+ uint16_t dst, uint32_t iv_index,
+ bool aszmic, uint8_t nonce[13]);
+bool mesh_crypto_application_encrypt(uint8_t akf, uint32_t seq, uint16_t src,
+ uint16_t dst, uint32_t iv_index,
+ const uint8_t app_key[16],
+ const uint8_t *aad, uint8_t aad_len,
+ const uint8_t *msg, uint8_t msg_len,
+ uint8_t *out, void *app_mic,
+ size_t mic_size);
+bool mesh_crypto_application_decrypt(uint8_t akf, uint32_t seq, uint16_t src,
+ uint16_t dst, uint32_t iv_index,
+ const uint8_t app_key[16],
+ const uint8_t *aad, uint8_t aad_len,
+ const uint8_t *enc_msg, uint8_t enc_msg_len,
+ uint8_t *out, void *app_mic, size_t mic_size);
+bool mesh_crypto_device_key(const uint8_t secret[32],
+ const uint8_t salt[16],
+ uint8_t device_key[16]);
+bool mesh_crypto_virtual_addr(const uint8_t virtual_label[16],
+ uint16_t *v_addr);
+bool mesh_crypto_nonce(const uint8_t secret[32],
+ const uint8_t salt[16],
+ uint8_t nonce[13]);
+bool mesh_crypto_k1(const uint8_t ikm[16], const uint8_t salt[16],
+ const void *info, size_t info_len, uint8_t okm[16]);
+bool mesh_crypto_k2(const uint8_t n[16], const uint8_t *p, size_t p_len,
+ uint8_t net_id[1],
+ uint8_t enc_key[16],
+ uint8_t priv_key[16]);
+bool mesh_crypto_k3(const uint8_t n[16], uint8_t out64[8]);
+bool mesh_crypto_k4(const uint8_t a[16], uint8_t out5[1]);
+bool mesh_crypto_s1(const void *info, size_t len, uint8_t salt[16]);
+bool mesh_crypto_prov_prov_salt(const uint8_t conf_salt[16],
+ const uint8_t prov_rand[16],
+ const uint8_t dev_rand[16],
+ uint8_t prov_salt[16]);
+bool mesh_crypto_prov_conf_key(const uint8_t secret[32],
+ const uint8_t salt[16],
+ uint8_t conf_key[16]);
+bool mesh_crypto_session_key(const uint8_t secret[32],
+ const uint8_t salt[16],
+ uint8_t session_key[16]);
+bool mesh_crypto_packet_encode(uint8_t *packet, uint8_t packet_len,
+ const uint8_t network_key[16],
+ uint32_t iv_index,
+ const uint8_t privacy_key[16]);
+bool mesh_crypto_packet_decode(const uint8_t *packet, uint8_t packet_len,
+ bool proxy, uint8_t *out, uint32_t iv_index,
+ const uint8_t network_key[16],
+ const uint8_t privacy_key[16]);
+
+bool mesh_crypto_aes_cmac(const uint8_t key[16], const uint8_t *msg,
+ size_t msg_len, uint8_t res[16]);
+
+bool mesh_get_random_bytes(void *buf, size_t num_bytes);
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2017 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>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <sys/uio.h>
+#include <wordexp.h>
+
+#include <glib.h>
+
+#include "src/shared/io.h"
+#include "src/shared/shell.h"
+#include "gdbus/gdbus.h"
+#include "lib/bluetooth.h"
+#include "lib/uuid.h"
+#include "mesh/node.h"
+#include "mesh/util.h"
+#include "mesh/gatt.h"
+#include "mesh/prov.h"
+#include "mesh/net.h"
+
+#define MESH_PROV_DATA_OUT_UUID_STR "00002adc-0000-1000-8000-00805f9b34fb"
+#define MESH_PROXY_DATA_OUT_UUID_STR "00002ade-0000-1000-8000-00805f9b34fb"
+
+static struct io *write_io;
+static uint16_t write_mtu;
+
+static struct io *notify_io;
+static uint16_t notify_mtu;
+
+struct write_data {
+ GDBusProxy *proxy;
+ void *user_data;
+ struct iovec iov;
+ GDBusReturnFunction cb;
+ uint8_t *gatt_data;
+ uint8_t gatt_len;
+};
+
+struct notify_data {
+ GDBusProxy *proxy;
+ bool enable;
+ GDBusReturnFunction cb;
+ void *user_data;
+};
+
+bool mesh_gatt_is_child(GDBusProxy *proxy, GDBusProxy *parent,
+ const char *name)
+{
+ DBusMessageIter iter;
+ const char *parent_path;
+
+ if (!parent)
+ return FALSE;
+
+ if (g_dbus_proxy_get_property(proxy, name, &iter) == FALSE)
+ return FALSE;
+
+ dbus_message_iter_get_basic(&iter, &parent_path);
+
+ if (!strcmp(parent_path, g_dbus_proxy_get_path(parent)))
+ return TRUE;
+ else
+ return FALSE;
+}
+
+/* Refactor this once actual MTU is available */
+#define GATT_MTU 23
+
+static void write_data_free(void *user_data)
+{
+ struct write_data *data = user_data;
+
+ g_free(data->gatt_data);
+ free(data);
+}
+
+uint16_t mesh_gatt_sar(uint8_t **pkt, uint16_t size)
+{
+ const uint8_t *data = *pkt;
+ uint8_t gatt_hdr = *data++;
+ uint8_t type = gatt_hdr & GATT_TYPE_MASK;
+ static uint8_t gatt_size;
+ static uint8_t gatt_pkt[67];
+
+ print_byte_array("GATT-RX:\t", *pkt, size);
+ if (size < 1) {
+ gatt_pkt[0] = GATT_TYPE_INVALID;
+ /* TODO: Disconnect GATT per last paragraph sec 6.6 */
+ return 0;
+ }
+
+ size--;
+
+ switch (gatt_hdr & GATT_SAR_MASK) {
+ case GATT_SAR_FIRST:
+ gatt_size = 1;
+ gatt_pkt[0] = type;
+ /* TODO: Start Proxy Timeout */
+ /* fall through */
+
+ case GATT_SAR_CONTINUE:
+ if (gatt_pkt[0] != type ||
+ gatt_size + size > MAX_GATT_SIZE) {
+
+ /* Invalidate packet and return failure */
+ gatt_pkt[0] = GATT_TYPE_INVALID;
+ /* TODO: Disconnect GATT per last paragraph sec 6.6 */
+ return 0;
+ }
+
+ memcpy(gatt_pkt + gatt_size, data, size);
+ gatt_size += size;
+
+ /* We are good to this point, but incomplete */
+ return 0;
+
+ default:
+ case GATT_SAR_COMPLETE:
+ gatt_size = 1;
+ gatt_pkt[0] = type;
+
+ /* fall through */
+
+ case GATT_SAR_LAST:
+ if (gatt_pkt[0] != type ||
+ gatt_size + size > MAX_GATT_SIZE) {
+
+ /* Invalidate packet and return failure */
+ gatt_pkt[0] = GATT_TYPE_INVALID;
+ /* Disconnect GATT per last paragraph sec 6.6 */
+ return 0;
+ }
+
+ memcpy(gatt_pkt + gatt_size, data, size);
+ gatt_size += size;
+ *pkt = gatt_pkt;
+ return gatt_size;
+ }
+}
+
+static bool pipe_write(struct io *io, void *user_data)
+{
+ struct write_data *data = user_data;
+ struct iovec iov[2];
+ uint8_t sar;
+ uint8_t max_len;
+
+ if (data == NULL)
+ return true;
+
+ max_len = write_mtu ? write_mtu - 3 - 1 : GATT_MTU - 3 - 1;
+ print_byte_array("GATT-TX:\t", data->gatt_data, data->gatt_len);
+
+ sar = data->gatt_data[0];
+
+ data->iov.iov_base = data->gatt_data + 1;
+ data->iov.iov_len--;
+
+ sar = data->gatt_data[0] & GATT_TYPE_MASK;
+ data->gatt_len--;
+
+ if (data->gatt_len > max_len) {
+ sar |= GATT_SAR_FIRST;
+ data->iov.iov_len = max_len;
+ }
+
+ iov[0].iov_base = &sar;
+ iov[0].iov_len = sizeof(sar);
+
+ while (1) {
+ int err;
+
+ iov[1] = data->iov;
+
+ err = io_send(io, iov, 2);
+ if (err < 0) {
+ bt_shell_printf("Failed to write: %s\n", strerror(-err));
+ write_data_free(data);
+ return false;
+ }
+
+ switch (sar & GATT_SAR_MASK) {
+ case GATT_SAR_FIRST:
+ case GATT_SAR_CONTINUE:
+ data->gatt_len -= max_len;
+ data->iov.iov_base = data->iov.iov_base + max_len;
+
+ sar &= GATT_TYPE_MASK;
+ if (max_len < data->gatt_len) {
+ data->iov.iov_len = max_len;
+ sar |= GATT_SAR_CONTINUE;
+ } else {
+ data->iov.iov_len = data->gatt_len;
+ sar |= GATT_SAR_LAST;
+ }
+
+ break;
+
+ default:
+ if(data->cb)
+ data->cb(NULL, data->user_data);
+ write_data_free(data);
+ return true;
+ }
+ }
+}
+
+static void write_io_destroy(void)
+{
+ io_destroy(write_io);
+ write_io = NULL;
+ write_mtu = 0;
+}
+
+static void notify_io_destroy(void)
+{
+ io_destroy(notify_io);
+ notify_io = NULL;
+ notify_mtu = 0;
+}
+
+static bool pipe_hup(struct io *io, void *user_data)
+{
+ bt_shell_printf("%s closed\n", io == notify_io ? "Notify" : "Write");
+
+ if (io == notify_io)
+ notify_io_destroy();
+ else
+ write_io_destroy();
+
+ return false;
+}
+
+static struct io *pipe_io_new(int fd)
+{
+ struct io *io;
+
+ io = io_new(fd);
+
+ io_set_close_on_destroy(io, true);
+
+ io_set_disconnect_handler(io, pipe_hup, NULL, NULL);
+
+ return io;
+}
+
+static void acquire_write_reply(DBusMessage *message, void *user_data)
+{
+ struct write_data *data = user_data;
+ DBusError error;
+ int fd;
+
+ dbus_error_init(&error);
+
+ if (dbus_set_error_from_message(&error, message) == TRUE) {
+ dbus_error_free(&error);
+ bt_shell_printf("Failed to write\n");
+ write_data_free(data);
+ return;
+ }
+
+ if ((dbus_message_get_args(message, NULL, DBUS_TYPE_UNIX_FD, &fd,
+ DBUS_TYPE_UINT16, &write_mtu,
+ DBUS_TYPE_INVALID) == false)) {
+ bt_shell_printf("Invalid AcquireWrite response\n");
+ return;
+ }
+
+ bt_shell_printf("AcquireWrite success: fd %d MTU %u\n", fd, write_mtu);
+
+ write_io = pipe_io_new(fd);
+
+ pipe_write(write_io, data);
+}
+
+static void acquire_setup(DBusMessageIter *iter, void *user_data)
+{
+ DBusMessageIter dict;
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING
+ DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+ &dict);
+
+ dbus_message_iter_close_container(iter, &dict);
+}
+
+bool mesh_gatt_write(GDBusProxy *proxy, uint8_t *buf, uint16_t len,
+ GDBusReturnFunction cb, void *user_data)
+{
+ struct write_data *data;
+
+ if (!buf || !len)
+ return false;
+
+ if (len > 69)
+ return false;
+
+ data = g_new0(struct write_data, 1);
+ if (!data)
+ return false;
+
+ /* TODO: should keep in queue in case we need to cancel write? */
+
+ data->gatt_len = len;
+ data->gatt_data = g_memdup(buf, len);
+ data->gatt_data[0] &= GATT_TYPE_MASK;
+ data->iov.iov_base = data->gatt_data;
+ data->iov.iov_len = len;
+ data->proxy = proxy;
+ data->user_data = user_data;
+ data->cb = cb;
+
+ if (write_io)
+ return pipe_write(write_io, data);
+
+ if (g_dbus_proxy_method_call(proxy, "AcquireWrite",
+ acquire_setup, acquire_write_reply,
+ data, NULL) == FALSE) {
+ bt_shell_printf("Failed to AcquireWrite\n");
+ write_data_free(data);
+ return false;
+ }
+ return true;
+}
+
+static void notify_reply(DBusMessage *message, void *user_data)
+{
+ struct notify_data *data = user_data;
+ DBusError error;
+
+ dbus_error_init(&error);
+
+ if (dbus_set_error_from_message(&error, message) == TRUE) {
+ bt_shell_printf("Failed to %s notify: %s\n",
+ data->enable ? "start" : "stop", error.name);
+ dbus_error_free(&error);
+ goto done;
+ }
+
+ bt_shell_printf("Notify %s\n", data->enable ? "started" : "stopped");
+
+done:
+ if (data->cb)
+ data->cb(message, data->user_data);
+
+ g_free(data);
+}
+
+static bool pipe_read(struct io *io, bool prov, void *user_data)
+{
+ struct mesh_node *node = user_data;
+ uint8_t buf[512];
+ uint8_t *res;
+ int fd = io_get_fd(io);
+ ssize_t len;
+
+ if (io != notify_io)
+ return true;
+
+ while ((len = read(fd, buf, sizeof(buf)))) {
+ if (len <= 0)
+ break;
+
+ res = buf;
+ mesh_gatt_sar(&res, len);
+
+ if (prov)
+ prov_data_ready(node, res, len);
+ else
+ net_data_ready(res, len);
+ }
+
+ return true;
+}
+
+static bool pipe_read_prov(struct io *io, void *user_data)
+{
+ return pipe_read(io, true, user_data);
+}
+
+static bool pipe_read_proxy(struct io *io, void *user_data)
+{
+ return pipe_read(io, false, user_data);
+}
+
+static void acquire_notify_reply(DBusMessage *message, void *user_data)
+{
+ struct notify_data *data = user_data;
+ DBusMessageIter iter;
+ DBusError error;
+ int fd;
+ const char *uuid;
+
+ dbus_error_init(&error);
+
+ if (dbus_set_error_from_message(&error, message) == TRUE) {
+ dbus_error_free(&error);
+ if (g_dbus_proxy_method_call(data->proxy, "StartNotify", NULL,
+ notify_reply, data, NULL) == FALSE) {
+ bt_shell_printf("Failed to StartNotify\n");
+ g_free(data);
+ }
+ return;
+ }
+
+ if (notify_io) {
+ io_destroy(notify_io);
+ notify_io = NULL;
+ }
+
+ notify_mtu = 0;
+
+ if ((dbus_message_get_args(message, NULL, DBUS_TYPE_UNIX_FD, &fd,
+ DBUS_TYPE_UINT16, ¬ify_mtu,
+ DBUS_TYPE_INVALID) == false)) {
+ if (g_dbus_proxy_method_call(data->proxy, "StartNotify", NULL,
+ notify_reply, data, NULL) == FALSE) {
+ bt_shell_printf("Failed to StartNotify\n");
+ g_free(data);
+ }
+ return;
+ }
+
+ bt_shell_printf("AcquireNotify success: fd %d MTU %u\n", fd, notify_mtu);
+
+ if (g_dbus_proxy_get_property(data->proxy, "UUID", &iter) == FALSE)
+ goto done;
+
+ notify_io = pipe_io_new(fd);
+
+ dbus_message_iter_get_basic(&iter, &uuid);
+
+ if (!bt_uuid_strcmp(uuid, MESH_PROV_DATA_OUT_UUID_STR))
+ io_set_read_handler(notify_io, pipe_read_prov, data->user_data,
+ NULL);
+ else if (!bt_uuid_strcmp(uuid, MESH_PROXY_DATA_OUT_UUID_STR))
+ io_set_read_handler(notify_io, pipe_read_proxy, data->user_data,
+ NULL);
+
+done:
+ if (data->cb)
+ data->cb(message, data->user_data);
+
+ g_free(data);
+}
+
+bool mesh_gatt_notify(GDBusProxy *proxy, bool enable, GDBusReturnFunction cb,
+ void *user_data)
+{
+ struct notify_data *data;
+ DBusMessageIter iter;
+ const char *method;
+ GDBusSetupFunction setup = NULL;
+
+ data = g_new0(struct notify_data, 1);
+ data->proxy = proxy;
+ data->enable = enable;
+ data->cb = cb;
+ data->user_data = user_data;
+
+ if (enable == TRUE) {
+ if (g_dbus_proxy_get_property(proxy, "NotifyAcquired", &iter)) {
+ method = "AcquireNotify";
+ cb = acquire_notify_reply;
+ setup = acquire_setup;
+ } else {
+ method = "StartNotify";
+ cb = notify_reply;
+ }
+ } else {
+ if (notify_io) {
+ notify_io_destroy();
+ if (cb)
+ cb(NULL, user_data);
+ return true;
+ } else {
+ method = "StopNotify";
+ cb = notify_reply;
+ }
+ }
+
+ if (g_dbus_proxy_method_call(proxy, method, setup, cb,
+ data, NULL) == FALSE) {
+ bt_shell_printf("Failed to %s\n", method);
+ return false;
+ }
+ return true;
+}
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2017 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
+ *
+ */
+
+#include "gdbus/gdbus.h"
+
+/* Largest Possible GATT Packet: Provisioning Public Key + type + sar */
+#define MAX_GATT_SIZE (64 + 1 + 1)
+
+#define GATT_SAR_MASK 0xc0
+#define GATT_SAR_COMPLETE 0x00
+#define GATT_SAR_FIRST 0x40
+#define GATT_SAR_CONTINUE 0x80
+#define GATT_SAR_LAST 0xc0
+#define GATT_TYPE_INVALID 0xff
+#define GATT_TYPE_MASK 0x3f
+
+uint16_t mesh_gatt_sar(uint8_t **pkt, uint16_t size);
+bool mesh_gatt_is_child(GDBusProxy *proxy, GDBusProxy *parent,
+ const char *name);
+bool mesh_gatt_write(GDBusProxy *proxy, uint8_t *buf, uint16_t len,
+ GDBusReturnFunction cb, void *user_data);
+bool mesh_gatt_notify(GDBusProxy *proxy, bool enable, GDBusReturnFunction cb,
+ void *user_data);
+void mesh_gatt_cleanup(void);
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2017 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
+ *
+ */
+
+#define KR_PHASE_NONE 0x00
+#define KR_PHASE_ONE 0x01
+#define KR_PHASE_TWO 0x02
+#define KR_PHASE_INVALID 0xff
+
+bool keys_app_key_add(uint16_t net_idx, uint16_t app_idx, uint8_t *key,
+ bool update);
+bool keys_net_key_add(uint16_t index, uint8_t *key, bool update);
+uint16_t keys_app_key_get_bound(uint16_t app_idx);
+uint8_t *keys_app_key_get(uint16_t app_idx, bool current);
+uint8_t *keys_net_key_get(uint16_t net_idx, bool current);
+bool keys_app_key_delete(uint16_t app_idx);
+bool keys_net_key_delete(uint16_t net_idx);
+uint8_t keys_get_kr_phase(uint16_t net_idx);
+bool keys_set_kr_phase(uint16_t index, uint8_t phase);
+void keys_cleanup_all(void);
--- /dev/null
+{
+ "$schema":"file:\/\/\/BlueZ\/Mesh\/local_schema\/mesh.jsonschema",
+ "meshName":"BT Mesh",
+ "netKeys":[
+ {
+ "index": 0,
+ "keyRefresh": 0
+ }
+ ],
+ "appKeys":[
+ {
+ "index": 0,
+ "boundNetKey": 0
+ },
+ {
+ "index": 1,
+ "boundNetKey": 0
+ }
+ ],
+"node": {
+ "IVindex":"00000005",
+ "IVupdate":"0",
+ "sequenceNumber": 0,
+ "composition": {
+ "cid": "0002",
+ "pid": "0010",
+ "vid": "0001",
+ "crpl": "000a",
+ "features": {
+ "relay": false,
+ "proxy": true,
+ "friend": false,
+ "lowPower": false
+ },
+ "elements": [
+ {
+ "elementIndex": 0,
+ "location": "0001",
+ "models": ["0000", "0001", "1001"]
+ }
+ ]
+ },
+ "configuration":{
+ "netKeys": [0],
+ "appKeys": [ 0, 1],
+ "defaultTTL": 10,
+ "elements": [
+ {
+ "elementIndex": 0,
+ "unicastAddress":"0077",
+ "models": [
+ {
+ "modelId": "1001",
+ "bind": [1]
+ }
+ ]
+ }
+ ]
+ }
+ }
+}
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2017 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>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <wordexp.h>
+
+#include <inttypes.h>
+#include <ctype.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include "bluetooth/bluetooth.h"
+
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/uuid.h"
+#include "src/shared/shell.h"
+#include "src/shared/util.h"
+#include "gdbus/gdbus.h"
+#include "mesh/mesh-net.h"
+#include "mesh/gatt.h"
+#include "mesh/crypto.h"
+#include "mesh/node.h"
+#include "mesh/net.h"
+#include "mesh/keys.h"
+#include "mesh/prov.h"
+#include "mesh/util.h"
+#include "mesh/agent.h"
+#include "mesh/prov-db.h"
+#include "mesh/config-model.h"
+#include "mesh/onoff-model.h"
+
+/* String display constants */
+#define COLORED_NEW COLOR_GREEN "NEW" COLOR_OFF
+#define COLORED_CHG COLOR_YELLOW "CHG" COLOR_OFF
+#define COLORED_DEL COLOR_RED "DEL" COLOR_OFF
+
+#define PROMPT_ON COLOR_BLUE "[meshctl]" COLOR_OFF "# "
+#define PROMPT_OFF "Waiting to connect to bluetoothd..."
+
+#define MESH_PROV_DATA_IN_UUID_STR "00002adb-0000-1000-8000-00805f9b34fb"
+#define MESH_PROV_DATA_OUT_UUID_STR "00002adc-0000-1000-8000-00805f9b34fb"
+#define MESH_PROXY_DATA_IN_UUID_STR "00002add-0000-1000-8000-00805f9b34fb"
+#define MESH_PROXY_DATA_OUT_UUID_STR "00002ade-0000-1000-8000-00805f9b34fb"
+
+static DBusConnection *dbus_conn;
+
+struct adapter {
+GDBusProxy *proxy;
+ GList *mesh_devices;
+};
+
+struct mesh_device {
+ GDBusProxy *proxy;
+ uint8_t dev_uuid[16];
+ gboolean hide;
+};
+
+GList *service_list;
+GList *char_list;
+
+static GList *ctrl_list;
+static struct adapter *default_ctrl;
+
+static char *mesh_prov_db_filename;
+static char *mesh_local_config_filename;
+
+static bool discovering = false;
+static bool discover_mesh;
+static uint16_t prov_net_key_index = NET_IDX_PRIMARY;
+static const struct bt_shell_menu main_menu;
+
+#define CONN_TYPE_NETWORK 0x00
+#define CONN_TYPE_IDENTITY 0x01
+#define CONN_TYPE_PROVISION 0x02
+#define CONN_TYPE_INVALID 0xff
+
+#define NET_IDX_INVALID 0xffff
+
+struct {
+ GDBusProxy *device;
+ GDBusProxy *service;
+ GDBusProxy *data_in;
+ GDBusProxy *data_out;
+ bool session_open;
+ uint16_t unicast;
+ uint16_t net_idx;
+ uint8_t dev_uuid[16];
+ uint8_t type;
+} connection;
+
+static bool service_is_mesh(GDBusProxy *proxy, const char *target_uuid)
+{
+ DBusMessageIter iter;
+ const char *uuid;
+
+ if (g_dbus_proxy_get_property(proxy, "UUID", &iter) == FALSE)
+ return false;
+
+ dbus_message_iter_get_basic(&iter, &uuid);
+
+ if (target_uuid)
+ return (!bt_uuid_strcmp(uuid, target_uuid));
+ else if (bt_uuid_strcmp(uuid, MESH_PROV_SVC_UUID) ||
+ bt_uuid_strcmp(uuid, MESH_PROXY_SVC_UUID))
+ return true;
+ else
+ return false;
+}
+
+static bool char_is_mesh(GDBusProxy *proxy, const char *target_uuid)
+{
+ DBusMessageIter iter;
+ const char *uuid;
+
+ if (g_dbus_proxy_get_property(proxy, "UUID", &iter) == FALSE)
+ return false;
+
+ dbus_message_iter_get_basic(&iter, &uuid);
+
+ if (target_uuid)
+ return (!bt_uuid_strcmp(uuid, target_uuid));
+
+ if (!bt_uuid_strcmp(uuid, MESH_PROV_DATA_IN_UUID_STR))
+ return true;
+
+ if (!bt_uuid_strcmp(uuid, MESH_PROV_DATA_OUT_UUID_STR))
+ return true;
+
+ if (!bt_uuid_strcmp(uuid, MESH_PROXY_DATA_IN_UUID_STR))
+ return true;
+
+ if (!bt_uuid_strcmp(uuid, MESH_PROXY_DATA_OUT_UUID_STR))
+ return true;
+
+ return false;
+}
+
+static gboolean check_default_ctrl(void)
+{
+ if (!default_ctrl) {
+ bt_shell_printf("No default controller available\n");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void proxy_leak(gpointer data)
+{
+ bt_shell_printf("Leaking proxy %p\n", data);
+}
+
+static void connect_handler(DBusConnection *connection, void *user_data)
+{
+ bt_shell_set_prompt(PROMPT_ON);
+}
+
+static void disconnect_handler(DBusConnection *connection, void *user_data)
+{
+ bt_shell_detach();
+
+ bt_shell_set_prompt(PROMPT_OFF);
+
+ g_list_free_full(ctrl_list, proxy_leak);
+ ctrl_list = NULL;
+
+ default_ctrl = NULL;
+}
+
+static void print_adapter(GDBusProxy *proxy, const char *description)
+{
+ DBusMessageIter iter;
+ const char *address, *name;
+
+ if (g_dbus_proxy_get_property(proxy, "Address", &iter) == FALSE)
+ return;
+
+ dbus_message_iter_get_basic(&iter, &address);
+
+ if (g_dbus_proxy_get_property(proxy, "Alias", &iter) == TRUE)
+ dbus_message_iter_get_basic(&iter, &name);
+ else
+ name = "<unknown>";
+
+ bt_shell_printf("%s%s%sController %s %s %s\n",
+ description ? "[" : "",
+ description ? : "",
+ description ? "] " : "",
+ address, name,
+ default_ctrl &&
+ default_ctrl->proxy == proxy ?
+ "[default]" : "");
+
+}
+
+static void print_device(GDBusProxy *proxy, const char *description)
+{
+ DBusMessageIter iter;
+ const char *address, *name;
+
+ if (g_dbus_proxy_get_property(proxy, "Address", &iter) == FALSE)
+ return;
+
+ dbus_message_iter_get_basic(&iter, &address);
+
+ if (g_dbus_proxy_get_property(proxy, "Alias", &iter) == TRUE)
+ dbus_message_iter_get_basic(&iter, &name);
+ else
+ name = "<unknown>";
+
+ bt_shell_printf("%s%s%sDevice %s %s\n",
+ description ? "[" : "",
+ description ? : "",
+ description ? "] " : "",
+ address, name);
+}
+
+static void print_iter(const char *label, const char *name,
+ DBusMessageIter *iter)
+{
+ dbus_bool_t valbool;
+ dbus_uint32_t valu32;
+ dbus_uint16_t valu16;
+ dbus_int16_t vals16;
+ unsigned char byte;
+ const char *valstr;
+ DBusMessageIter subiter;
+ char *entry;
+
+ if (iter == NULL) {
+ bt_shell_printf("%s%s is nil\n", label, name);
+ return;
+ }
+
+ switch (dbus_message_iter_get_arg_type(iter)) {
+ case DBUS_TYPE_INVALID:
+ bt_shell_printf("%s%s is invalid\n", label, name);
+ break;
+ case DBUS_TYPE_STRING:
+ case DBUS_TYPE_OBJECT_PATH:
+ dbus_message_iter_get_basic(iter, &valstr);
+ bt_shell_printf("%s%s: %s\n", label, name, valstr);
+ break;
+ case DBUS_TYPE_BOOLEAN:
+ dbus_message_iter_get_basic(iter, &valbool);
+ bt_shell_printf("%s%s: %s\n", label, name,
+ valbool == TRUE ? "yes" : "no");
+ break;
+ case DBUS_TYPE_UINT32:
+ dbus_message_iter_get_basic(iter, &valu32);
+ bt_shell_printf("%s%s: 0x%06x\n", label, name, valu32);
+ break;
+ case DBUS_TYPE_UINT16:
+ dbus_message_iter_get_basic(iter, &valu16);
+ bt_shell_printf("%s%s: 0x%04x\n", label, name, valu16);
+ break;
+ case DBUS_TYPE_INT16:
+ dbus_message_iter_get_basic(iter, &vals16);
+ bt_shell_printf("%s%s: %d\n", label, name, vals16);
+ break;
+ case DBUS_TYPE_BYTE:
+ dbus_message_iter_get_basic(iter, &byte);
+ bt_shell_printf("%s%s: 0x%02x\n", label, name, byte);
+ break;
+ case DBUS_TYPE_VARIANT:
+ dbus_message_iter_recurse(iter, &subiter);
+ print_iter(label, name, &subiter);
+ break;
+ case DBUS_TYPE_ARRAY:
+ dbus_message_iter_recurse(iter, &subiter);
+ while (dbus_message_iter_get_arg_type(&subiter) !=
+ DBUS_TYPE_INVALID) {
+ print_iter(label, name, &subiter);
+ dbus_message_iter_next(&subiter);
+ }
+ break;
+ case DBUS_TYPE_DICT_ENTRY:
+ dbus_message_iter_recurse(iter, &subiter);
+ entry = g_strconcat(name, "Key", NULL);
+ print_iter(label, entry, &subiter);
+ g_free(entry);
+
+ entry = g_strconcat(name, " Value", NULL);
+ dbus_message_iter_next(&subiter);
+ print_iter(label, entry, &subiter);
+ g_free(entry);
+ break;
+ default:
+ bt_shell_printf("%s%s has unsupported type\n", label, name);
+ break;
+ }
+}
+
+static void print_property(GDBusProxy *proxy, const char *name)
+{
+ DBusMessageIter iter;
+
+ if (g_dbus_proxy_get_property(proxy, name, &iter) == FALSE)
+ return;
+
+ print_iter("\t", name, &iter);
+}
+
+static void forget_mesh_devices()
+{
+ g_list_free_full(default_ctrl->mesh_devices, g_free);
+ default_ctrl->mesh_devices = NULL;
+}
+
+static struct mesh_device *find_device_by_uuid(GList *source, uint8_t uuid[16])
+{
+ GList *list;
+
+ for (list = g_list_first(source); list; list = g_list_next(list)) {
+ struct mesh_device *dev = list->data;
+
+ if (!memcmp(dev->dev_uuid, uuid, 16))
+ return dev;
+ }
+
+ return NULL;
+}
+
+static void print_prov_service(struct prov_svc_data *prov_data)
+{
+ const char *prefix = "\t\t";
+ char txt_uuid[16 * 2 + 1];
+ int i;
+
+ bt_shell_printf("%sMesh Provisioning Service (%s)\n", prefix,
+ MESH_PROV_SVC_UUID);
+ for (i = 0; i < 16; ++i) {
+ sprintf(txt_uuid + (i * 2), "%2.2x", prov_data->dev_uuid[i]);
+ }
+
+ bt_shell_printf("%s\tDevice UUID: %s\n", prefix, txt_uuid);
+ bt_shell_printf("%s\tOOB: %4.4x\n", prefix, prov_data->oob);
+
+}
+
+static bool parse_prov_service_data(const char *uuid, uint8_t *data, int len,
+ void *data_out)
+{
+ struct prov_svc_data *prov_data = data_out;
+ int i;
+
+ if (len < 18)
+ return false;
+
+ for (i = 0; i < 16; ++i) {
+ prov_data->dev_uuid[i] = data[i];
+ }
+
+ prov_data->oob = get_be16(&data[16]);
+
+ return true;
+}
+
+static bool parse_mesh_service_data(const char *uuid, uint8_t *data, int len,
+ void *data_out)
+{
+ const char *prefix = "\t\t";
+
+ if (!(len == 9 && data[0] == 0x00) && !(len == 17 && data[0] == 0x01)) {
+ bt_shell_printf("Unexpected mesh proxy service data length %d\n",
+ len);
+ return false;
+ }
+
+ if (data[0] != connection.type)
+ return false;
+
+ if (data[0] == CONN_TYPE_IDENTITY) {
+ uint8_t *key;
+
+ if (IS_UNASSIGNED(connection.unicast)) {
+ /* This would be a bug */
+ bt_shell_printf("Error: Searching identity with "
+ "unicast 0000\n");
+ return false;
+ }
+
+ key = keys_net_key_get(prov_net_key_index, true);
+ if (!key)
+ return false;
+
+ if (!mesh_crypto_identity_check(key, connection.unicast,
+ &data[1]))
+ return false;
+
+ if (discovering) {
+ bt_shell_printf("\n%sMesh Proxy Service (%s)\n", prefix,
+ uuid);
+ bt_shell_printf("%sIdentity for node %4.4x\n", prefix,
+ connection.unicast);
+ }
+
+ } else if (data[0] == CONN_TYPE_NETWORK) {
+ uint16_t net_idx = net_validate_proxy_beacon(data + 1);
+
+ if (net_idx == NET_IDX_INVALID || net_idx != connection.net_idx)
+ return false;
+
+ if (discovering) {
+ bt_shell_printf("\n%sMesh Proxy Service (%s)\n", prefix,
+ uuid);
+ bt_shell_printf("%sNetwork Beacon for net index %4.4x\n",
+ prefix, net_idx);
+ }
+ }
+
+ return true;
+}
+
+static bool parse_service_data(GDBusProxy *proxy, const char *target_uuid,
+ void *data_out)
+{
+ DBusMessageIter iter, entries;
+ bool mesh_prov = false;
+ bool mesh_proxy = false;
+
+ if (target_uuid) {
+ mesh_prov = !strcmp(target_uuid, MESH_PROV_SVC_UUID);
+ mesh_proxy = !strcmp(target_uuid, MESH_PROXY_SVC_UUID);
+ }
+
+ if (!g_dbus_proxy_get_property(proxy, "ServiceData", &iter))
+ return false;
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
+ return false;
+
+ dbus_message_iter_recurse(&iter, &entries);
+
+ while (dbus_message_iter_get_arg_type(&entries)
+ == DBUS_TYPE_DICT_ENTRY) {
+ DBusMessageIter value, entry, array;
+ const char *uuid_str;
+ bt_uuid_t uuid;
+ uint8_t *service_data;
+ int len;
+
+ dbus_message_iter_recurse(&entries, &entry);
+ dbus_message_iter_get_basic(&entry, &uuid_str);
+
+ if (bt_string_to_uuid(&uuid, uuid_str) < 0)
+ goto fail;
+
+ dbus_message_iter_next(&entry);
+
+ if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_VARIANT)
+ goto fail;
+
+ dbus_message_iter_recurse(&entry, &value);
+
+ if (dbus_message_iter_get_arg_type(&value) != DBUS_TYPE_ARRAY)
+ goto fail;
+
+ dbus_message_iter_recurse(&value, &array);
+
+ if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_BYTE)
+ goto fail;
+
+ dbus_message_iter_get_fixed_array(&array, &service_data, &len);
+
+ if (mesh_prov && !strcmp(uuid_str, MESH_PROV_SVC_UUID)) {
+ return parse_prov_service_data(uuid_str, service_data,
+ len, data_out);
+ } else if (mesh_proxy &&
+ !strcmp(uuid_str, MESH_PROXY_SVC_UUID)) {
+ return parse_mesh_service_data(uuid_str, service_data,
+ len, data_out);
+ }
+
+ dbus_message_iter_next(&entries);
+ }
+
+ if (!target_uuid)
+ return true;
+fail:
+ return false;
+}
+
+static void print_uuids(GDBusProxy *proxy)
+{
+ DBusMessageIter iter, value;
+
+ if (g_dbus_proxy_get_property(proxy, "UUIDs", &iter) == FALSE)
+ return;
+
+ dbus_message_iter_recurse(&iter, &value);
+
+ while (dbus_message_iter_get_arg_type(&value) == DBUS_TYPE_STRING) {
+ const char *uuid, *text;
+
+ dbus_message_iter_get_basic(&value, &uuid);
+
+ text = bt_uuidstr_to_str(uuid);
+ if (text) {
+ char str[26];
+ unsigned int n;
+
+ str[sizeof(str) - 1] = '\0';
+
+ n = snprintf(str, sizeof(str), "%s", text);
+ if (n > sizeof(str) - 1) {
+ str[sizeof(str) - 2] = '.';
+ str[sizeof(str) - 3] = '.';
+ if (str[sizeof(str) - 4] == ' ')
+ str[sizeof(str) - 4] = '.';
+
+ n = sizeof(str) - 1;
+ }
+
+ bt_shell_printf("\tUUID: %s%*c(%s)\n",
+ str, 26 - n, ' ', uuid);
+ } else
+ bt_shell_printf("\tUUID: %*c(%s)\n", 26, ' ', uuid);
+
+ dbus_message_iter_next(&value);
+ }
+}
+
+static gboolean device_is_child(GDBusProxy *device, GDBusProxy *master)
+{
+ DBusMessageIter iter;
+ const char *adapter, *path;
+
+ if (!master)
+ return FALSE;
+
+ if (g_dbus_proxy_get_property(device, "Adapter", &iter) == FALSE)
+ return FALSE;
+
+ dbus_message_iter_get_basic(&iter, &adapter);
+ path = g_dbus_proxy_get_path(master);
+
+ if (!strcmp(path, adapter))
+ return TRUE;
+
+ return FALSE;
+}
+
+static struct adapter *find_parent(GDBusProxy *device)
+{
+ GList *list;
+
+ for (list = g_list_first(ctrl_list); list; list = g_list_next(list)) {
+ struct adapter *adapter = list->data;
+
+ if (device_is_child(device, adapter->proxy) == TRUE)
+ return adapter;
+ }
+ return NULL;
+}
+
+static void set_connected_device(GDBusProxy *proxy)
+{
+ char *desc = NULL;
+ DBusMessageIter iter;
+ char buf[10];
+ bool mesh;
+
+ connection.device = proxy;
+
+ if (proxy == NULL) {
+ memset(&connection, 0, sizeof(connection));
+ connection.type = CONN_TYPE_INVALID;
+ goto done;
+ }
+
+ if (connection.type == CONN_TYPE_IDENTITY) {
+ mesh = true;
+ snprintf(buf, 10, "Node-%4.4x", connection.unicast);
+ } else if (connection.type == CONN_TYPE_NETWORK) {
+ mesh = true;
+ snprintf(buf, 9, "Net-%4.4x", connection.net_idx);
+ } else {
+ mesh = false;
+ }
+
+ if (!g_dbus_proxy_get_property(proxy, "Alias", &iter) && !mesh)
+ goto done;
+
+ dbus_message_iter_get_basic(&iter, &desc);
+ desc = g_strdup_printf(COLOR_BLUE "[%s%s%s]" COLOR_OFF "# ", desc,
+ (desc && mesh) ? "-" : "",
+ mesh ? buf : "");
+
+done:
+ bt_shell_set_prompt(desc ? desc : PROMPT_ON);
+ g_free(desc);
+
+ /* If disconnected, return to main menu */
+ if (proxy == NULL)
+ bt_shell_set_menu(&main_menu);
+}
+
+static void connect_reply(DBusMessage *message, void *user_data)
+{
+ GDBusProxy *proxy = user_data;
+ DBusError error;
+
+ dbus_error_init(&error);
+
+ if (dbus_set_error_from_message(&error, message) == TRUE) {
+ bt_shell_printf("Failed to connect: %s\n", error.name);
+ dbus_error_free(&error);
+ set_connected_device(NULL);
+ return;
+ }
+
+ bt_shell_printf("Connection successful\n");
+
+ set_connected_device(proxy);
+}
+
+static void update_device_info(GDBusProxy *proxy)
+{
+ struct adapter *adapter = find_parent(proxy);
+ DBusMessageIter iter;
+ struct prov_svc_data prov_data;
+
+ if (!adapter) {
+ /* TODO: Error */
+ return;
+ }
+
+ if (adapter != default_ctrl)
+ return;
+
+ if (!g_dbus_proxy_get_property(proxy, "Address", &iter))
+ return;
+
+ if (parse_service_data(proxy, MESH_PROV_SVC_UUID, &prov_data)) {
+ struct mesh_device *dev;
+
+ dev = find_device_by_uuid(adapter->mesh_devices,
+ prov_data.dev_uuid);
+
+ /* Display provisioning service once per discovery session */
+ if (discovering && (!dev || !dev->hide))
+ print_prov_service(&prov_data);
+
+ if (dev) {
+ dev->proxy = proxy;
+ dev->hide = discovering;
+ return;
+ }
+
+ dev = g_malloc0(sizeof(struct mesh_device));
+ if (!dev)
+ return;
+
+ dev->proxy = proxy;
+ dev->hide = discovering;
+
+ memcpy(dev->dev_uuid, prov_data.dev_uuid, 16);
+
+ adapter->mesh_devices = g_list_append(adapter->mesh_devices,
+ dev);
+ print_device(proxy, COLORED_NEW);
+
+ node_create_new(&prov_data);
+
+ } else if (parse_service_data(proxy, MESH_PROXY_SVC_UUID, NULL) &&
+ discover_mesh) {
+ bool res;
+
+ g_dbus_proxy_method_call(default_ctrl->proxy, "StopDiscovery",
+ NULL, NULL, NULL, NULL);
+ discover_mesh = false;
+
+ forget_mesh_devices();
+
+ res = g_dbus_proxy_method_call(proxy, "Connect", NULL,
+ connect_reply, proxy, NULL);
+
+ if (!res)
+ bt_shell_printf("Failed to connect to mesh\n");
+
+ else
+ bt_shell_printf("Trying to connect to mesh\n");
+
+ }
+}
+
+static void adapter_added(GDBusProxy *proxy)
+{
+ struct adapter *adapter = g_malloc0(sizeof(struct adapter));
+
+ adapter->proxy = proxy;
+ ctrl_list = g_list_append(ctrl_list, adapter);
+
+ if (!default_ctrl)
+ default_ctrl = adapter;
+
+ print_adapter(proxy, COLORED_NEW);
+}
+
+static void data_out_notify(GDBusProxy *proxy, bool enable,
+ GDBusReturnFunction cb)
+{
+ struct mesh_node *node;
+
+ node = node_find_by_uuid(connection.dev_uuid);
+
+ if (!mesh_gatt_notify(proxy, enable, cb, node))
+ bt_shell_printf("Failed to %s notification on %s\n", enable ?
+ "start" : "stop", g_dbus_proxy_get_path(proxy));
+ else
+ bt_shell_printf("%s notification on %s\n", enable ?
+ "Start" : "Stop", g_dbus_proxy_get_path(proxy));
+}
+
+struct disconnect_data {
+ GDBusReturnFunction cb;
+ void *data;
+};
+
+static void disconnect(GDBusReturnFunction cb, void *user_data)
+{
+ GDBusProxy *proxy;
+ DBusMessageIter iter;
+ const char *addr;
+
+ proxy = connection.device;
+ if (!proxy)
+ return;
+
+ if (g_dbus_proxy_method_call(proxy, "Disconnect", NULL, cb, user_data,
+ NULL) == FALSE) {
+ bt_shell_printf("Failed to disconnect\n");
+ return;
+ }
+
+ if (g_dbus_proxy_get_property(proxy, "Address", &iter) == TRUE)
+ dbus_message_iter_get_basic(&iter, &addr);
+
+ bt_shell_printf("Attempting to disconnect from %s\n", addr);
+}
+
+static void disc_notify_cb(DBusMessage *message, void *user_data)
+{
+ struct disconnect_data *disc_data = user_data;
+
+ disconnect(disc_data->cb, disc_data->data);
+
+ g_free(user_data);
+}
+
+static void disconnect_device(GDBusReturnFunction cb, void *user_data)
+{
+ DBusMessageIter iter;
+
+ net_session_close(connection.data_in);
+
+ /* Stop notificiation on prov_out or proxy out characteristics */
+ if (connection.data_out) {
+ if (g_dbus_proxy_get_property(connection.data_out, "Notifying",
+ &iter) == TRUE) {
+ struct disconnect_data *disc_data;
+ disc_data = g_malloc(sizeof(struct disconnect_data));
+ disc_data->cb = cb;
+ disc_data->data = user_data;
+
+ if (mesh_gatt_notify(connection.data_out, false,
+ disc_notify_cb, disc_data))
+ return;
+ }
+ }
+
+ disconnect(cb, user_data);
+}
+
+static void mesh_prov_done(void *user_data, int status);
+
+static void notify_prov_out_cb(DBusMessage *message, void *user_data)
+{
+ struct mesh_node *node = user_data;
+ DBusError error;
+
+ dbus_error_init(&error);
+
+ if (dbus_set_error_from_message(&error, message) == TRUE) {
+ bt_shell_printf("Failed to start notify: %s\n", error.name);
+ dbus_error_free(&error);
+ return;
+ }
+
+ bt_shell_printf("Notify for Mesh Provisioning Out Data started\n");
+
+ if (connection.type != CONN_TYPE_PROVISION) {
+ bt_shell_printf("Error: wrong connection type %d (expected %d)\n",
+ connection.type, CONN_TYPE_PROVISION);
+ return;
+ }
+
+ if (!connection.data_in) {
+ bt_shell_printf("Error: don't have mesh provisioning data in\n");
+ return;
+ }
+
+ if (!node) {
+ bt_shell_printf("Error: provisioning node not present\n");
+ return;
+ }
+
+ if(!prov_open(node, connection.data_in, prov_net_key_index,
+ mesh_prov_done, node))
+ {
+ bt_shell_printf("Failed to start provisioning\n");
+ node_free(node);
+ disconnect_device(NULL, NULL);
+ } else
+ bt_shell_printf("Initiated provisioning\n");
+
+}
+
+static void session_open_cb (int status)
+{
+ if (status) {
+ bt_shell_printf("Failed to open Mesh session\n");
+ disconnect_device(NULL, NULL);
+ return;
+ }
+
+ bt_shell_printf("Mesh session is open\n");
+
+ /* Get composition data for a newly provisioned node */
+ if (connection.type == CONN_TYPE_IDENTITY)
+ config_client_get_composition(connection.unicast);
+}
+
+static void notify_proxy_out_cb(DBusMessage *message, void *user_data)
+{
+ DBusError error;
+
+ dbus_error_init(&error);
+
+ if (dbus_set_error_from_message(&error, message) == TRUE) {
+ bt_shell_printf("Failed to start notify: %s\n", error.name);
+ dbus_error_free(&error);
+ return;
+ }
+
+ bt_shell_printf("Notify for Mesh Proxy Out Data started\n");
+
+ if (connection.type != CONN_TYPE_IDENTITY &&
+ connection.type != CONN_TYPE_NETWORK) {
+ bt_shell_printf("Error: wrong connection type %d "
+ "(expected %d or %d)\n", connection.type,
+ CONN_TYPE_IDENTITY, CONN_TYPE_NETWORK);
+ return;
+ }
+
+ if (!connection.data_in) {
+ bt_shell_printf("Error: don't have mesh proxy data in\n");
+ return;
+ }
+
+ bt_shell_printf("Trying to open mesh session\n");
+ net_session_open(connection.data_in, true, session_open_cb);
+ connection.session_open = true;
+}
+
+static GDBusProxy *get_characteristic(GDBusProxy *device, const char *char_uuid)
+{
+ GList *l;
+ GDBusProxy *service;
+ const char *svc_uuid;
+
+ if (connection.type == CONN_TYPE_PROVISION) {
+ svc_uuid = MESH_PROV_SVC_UUID;
+ } else {
+ svc_uuid = MESH_PROXY_SVC_UUID;
+ }
+ for (l = service_list; l; l = l->next) {
+ if (mesh_gatt_is_child(l->data, device, "Device") &&
+ service_is_mesh(l->data, svc_uuid))
+ break;
+ }
+
+ if (l)
+ service = l->data;
+ else {
+ bt_shell_printf("Mesh service not found\n");
+ return NULL;
+ }
+
+ for (l = char_list; l; l = l->next) {
+ if (mesh_gatt_is_child(l->data, service, "Service") &&
+ char_is_mesh(l->data, char_uuid)) {
+ bt_shell_printf("Found matching char: path %s, uuid %s\n",
+ g_dbus_proxy_get_path(l->data), char_uuid);
+ return l->data;
+ }
+ }
+ return NULL;
+}
+
+static void mesh_session_setup(GDBusProxy *proxy)
+{
+ if (connection.type == CONN_TYPE_PROVISION) {
+ connection.data_in = get_characteristic(proxy,
+ MESH_PROV_DATA_IN_UUID_STR);
+ if (!connection.data_in)
+ goto fail;
+
+ connection.data_out = get_characteristic(proxy,
+ MESH_PROV_DATA_OUT_UUID_STR);
+ if (!connection.data_out)
+ goto fail;
+
+ data_out_notify(connection.data_out, true, notify_prov_out_cb);
+
+ } else if (connection.type != CONN_TYPE_INVALID){
+
+ connection.data_in = get_characteristic(proxy,
+ MESH_PROXY_DATA_IN_UUID_STR);
+ if (!connection.data_in)
+ goto fail;
+
+ connection.data_out = get_characteristic(proxy,
+ MESH_PROXY_DATA_OUT_UUID_STR);
+ if (!connection.data_out)
+ goto fail;
+
+ data_out_notify(connection.data_out, true, notify_proxy_out_cb);
+ }
+
+ return;
+
+fail:
+
+ bt_shell_printf("Services resolved, mesh characteristics not found\n");
+}
+
+static void proxy_added(GDBusProxy *proxy, void *user_data)
+{
+ const char *interface;
+
+ interface = g_dbus_proxy_get_interface(proxy);
+
+ if (!strcmp(interface, "org.bluez.Device1")) {
+ update_device_info(proxy);
+
+ } else if (!strcmp(interface, "org.bluez.Adapter1")) {
+
+ adapter_added(proxy);
+
+ } else if (!strcmp(interface, "org.bluez.GattService1") &&
+ service_is_mesh(proxy, NULL)) {
+
+ bt_shell_printf("Service added %s\n", g_dbus_proxy_get_path(proxy));
+ service_list = g_list_append(service_list, proxy);
+
+ } else if (!strcmp(interface, "org.bluez.GattCharacteristic1") &&
+ char_is_mesh(proxy, NULL)) {
+
+ bt_shell_printf("Char added %s:\n", g_dbus_proxy_get_path(proxy));
+
+ char_list = g_list_append(char_list, proxy);
+ }
+}
+
+static void start_discovery_reply(DBusMessage *message, void *user_data)
+{
+ dbus_bool_t enable = GPOINTER_TO_UINT(user_data);
+ DBusError error;
+
+ dbus_error_init(&error);
+
+ if (dbus_set_error_from_message(&error, message) == TRUE) {
+ bt_shell_printf("Failed to %s discovery: %s\n",
+ enable == TRUE ? "start" : "stop", error.name);
+ dbus_error_free(&error);
+ return;
+ }
+
+ bt_shell_printf("Discovery %s\n", enable == TRUE ? "started" : "stopped");
+}
+
+static struct mesh_device *find_device_by_proxy(GList *source,
+ GDBusProxy *proxy)
+{
+ GList *list;
+
+ for (list = g_list_first(source); list; list = g_list_next(list)) {
+ struct mesh_device *dev = list->data;
+ GDBusProxy *proxy = dev->proxy;
+
+ if (dev->proxy == proxy)
+ return dev;
+ }
+
+ return NULL;
+}
+
+static void device_removed(GDBusProxy *proxy)
+{
+ struct adapter *adapter = find_parent(proxy);
+ struct mesh_device *dev;
+
+ if (!adapter) {
+ /* TODO: Error */
+ return;
+ }
+
+ dev = find_device_by_proxy(adapter->mesh_devices, proxy);
+ if (dev)
+ adapter->mesh_devices = g_list_remove(adapter->mesh_devices,
+ dev);
+
+ print_device(proxy, COLORED_DEL);
+
+ if (connection.device == proxy)
+ set_connected_device(NULL);
+
+}
+
+static void adapter_removed(GDBusProxy *proxy)
+{
+ GList *ll;
+ for (ll = g_list_first(ctrl_list); ll; ll = g_list_next(ll)) {
+ struct adapter *adapter = ll->data;
+
+ if (adapter->proxy == proxy) {
+ print_adapter(proxy, COLORED_DEL);
+
+ if (default_ctrl && default_ctrl->proxy == proxy) {
+ default_ctrl = NULL;
+ set_connected_device(NULL);
+ }
+
+ ctrl_list = g_list_remove_link(ctrl_list, ll);
+
+ g_list_free_full(adapter->mesh_devices, g_free);
+ g_free(adapter);
+ g_list_free(ll);
+ return;
+ }
+ }
+}
+
+static void proxy_removed(GDBusProxy *proxy, void *user_data)
+{
+ const char *interface;
+
+ interface = g_dbus_proxy_get_interface(proxy);
+
+ if (!strcmp(interface, "org.bluez.Device1")) {
+ device_removed(proxy);
+ } else if (!strcmp(interface, "org.bluez.Adapter1")) {
+ adapter_removed(proxy);
+ } else if (!strcmp(interface, "org.bluez.GattService1")) {
+ if (proxy == connection.service) {
+ if (service_is_mesh(proxy, MESH_PROXY_SVC_UUID)) {
+ data_out_notify(connection.data_out,
+ false, NULL);
+ net_session_close(connection.data_in);
+ }
+ connection.service = NULL;
+ connection.data_in = NULL;
+ connection.data_out = NULL;
+ }
+
+ service_list = g_list_remove(service_list, proxy);
+
+ } else if (!strcmp(interface, "org.bluez.GattCharacteristic1")) {
+ char_list = g_list_remove(char_list, proxy);
+ }
+}
+
+static int get_characteristic_value(DBusMessageIter *value, uint8_t *buf)
+{
+ DBusMessageIter array;
+ uint8_t *data;
+ int len;
+
+ if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_ARRAY)
+ return 0;
+
+ dbus_message_iter_recurse(value, &array);
+
+ if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_BYTE)
+ return 0;
+
+ dbus_message_iter_get_fixed_array(&array, &data, &len);
+ memcpy(buf, data, len);
+
+ return len;
+}
+
+static bool process_mesh_characteristic(GDBusProxy *proxy)
+{
+ DBusMessageIter iter;
+ const char *uuid;
+ uint8_t *res;
+ uint8_t buf[256];
+ bool is_prov;
+
+ if (g_dbus_proxy_get_property(proxy, "UUID", &iter) == FALSE)
+ return false;
+
+ dbus_message_iter_get_basic(&iter, &uuid);
+
+ if (g_dbus_proxy_get_property(proxy, "Value", &iter) == FALSE)
+ return false;
+
+ is_prov = !bt_uuid_strcmp(uuid, MESH_PROV_DATA_OUT_UUID_STR);
+
+ if (is_prov || !bt_uuid_strcmp(uuid, MESH_PROXY_DATA_OUT_UUID_STR))
+ {
+ struct mesh_node *node;
+ uint16_t len;
+
+ len = get_characteristic_value(&iter, buf);
+
+ if (!len || len > 69)
+ return false;
+
+ res = buf;
+ len = mesh_gatt_sar(&res, len);
+
+ if (!len)
+ return false;
+
+ if (is_prov) {
+ node = node_find_by_uuid(connection.dev_uuid);
+
+ if (!node) {
+ bt_shell_printf("Node not found?\n");
+ return false;
+ }
+
+ return prov_data_ready(node, res, len);
+ }
+
+ return net_data_ready(res, len);
+ }
+
+ return false;
+}
+
+
+static void property_changed(GDBusProxy *proxy, const char *name,
+ DBusMessageIter *iter, void *user_data)
+{
+ const char *interface;
+
+ interface = g_dbus_proxy_get_interface(proxy);
+
+ if (!strcmp(interface, "org.bluez.Device1")) {
+
+ if (default_ctrl && device_is_child(proxy,
+ default_ctrl->proxy) == TRUE) {
+
+ if (strcmp(name, "Connected") == 0) {
+ dbus_bool_t connected;
+ dbus_message_iter_get_basic(iter, &connected);
+
+ if (connected && connection.device == NULL)
+ set_connected_device(proxy);
+ else if (!connected &&
+ connection.device == proxy)
+ set_connected_device(NULL);
+ } else if ((strcmp(name, "Alias") == 0) &&
+ connection.device == proxy) {
+ /* Re-generate prompt */
+ set_connected_device(proxy);
+ } else if (!strcmp(name, "ServiceData")) {
+ update_device_info(proxy);
+ } else if (!strcmp(name, "ServicesResolved")) {
+ gboolean resolved;
+
+ dbus_message_iter_get_basic(iter, &resolved);
+
+ bt_shell_printf("Services resolved %s\n", resolved ?
+ "yes" : "no");
+
+ if (resolved)
+ mesh_session_setup(connection.device);
+ }
+
+ }
+ } else if (!strcmp(interface, "org.bluez.Adapter1")) {
+ DBusMessageIter addr_iter;
+ char *str;
+
+ bt_shell_printf("Adapter property changed \n");
+ if (g_dbus_proxy_get_property(proxy, "Address",
+ &addr_iter) == TRUE) {
+ const char *address;
+
+ dbus_message_iter_get_basic(&addr_iter, &address);
+ str = g_strdup_printf("[" COLORED_CHG
+ "] Controller %s ", address);
+ } else
+ str = g_strdup("");
+
+ if (strcmp(name, "Discovering") == 0) {
+ int temp;
+
+ dbus_message_iter_get_basic(iter, &temp);
+ discovering = !!temp;
+ }
+
+ print_iter(str, name, iter);
+ g_free(str);
+ } else if (!strcmp(interface, "org.bluez.GattService1")) {
+ bt_shell_printf("Service property changed %s\n",
+ g_dbus_proxy_get_path(proxy));
+ } else if (!strcmp(interface, "org.bluez.GattCharacteristic1")) {
+ bt_shell_printf("Characteristic property changed %s\n",
+ g_dbus_proxy_get_path(proxy));
+
+ if ((strcmp(name, "Value") == 0) &&
+ ((connection.type == CONN_TYPE_PROVISION) ||
+ connection.session_open))
+ process_mesh_characteristic(proxy);
+ }
+}
+
+static void message_handler(DBusConnection *connection,
+ DBusMessage *message, void *user_data)
+{
+ bt_shell_printf("[SIGNAL] %s.%s\n", dbus_message_get_interface(message),
+ dbus_message_get_member(message));
+}
+
+static struct adapter *find_ctrl_by_address(GList *source, const char *address)
+{
+ GList *list;
+
+ for (list = g_list_first(source); list; list = g_list_next(list)) {
+ struct adapter *adapter = list->data;
+ DBusMessageIter iter;
+ const char *str;
+
+ if (g_dbus_proxy_get_property(adapter->proxy,
+ "Address", &iter) == FALSE)
+ continue;
+
+ dbus_message_iter_get_basic(&iter, &str);
+
+ if (!strcmp(str, address))
+ return adapter;
+ }
+
+ return NULL;
+}
+
+static gboolean parse_argument_on_off(int argc, char *argv[],
+ dbus_bool_t *value)
+{
+ if (!strcmp(argv[1], "on") || !strcmp(argv[1], "yes")) {
+ *value = TRUE;
+ return TRUE;
+ }
+
+ if (!strcmp(argv[1], "off") || !strcmp(argv[1], "no")) {
+ *value = FALSE;
+ return TRUE;
+ }
+
+ bt_shell_printf("Invalid argument %s\n", argv[1]);
+ return FALSE;
+}
+
+static void cmd_list(int argc, char *argv[])
+{
+ GList *list;
+
+ for (list = g_list_first(ctrl_list); list; list = g_list_next(list)) {
+ struct adapter *adapter = list->data;
+ print_adapter(adapter->proxy, NULL);
+ }
+}
+
+static void cmd_show(int argc, char *argv[])
+{
+ struct adapter *adapter;
+ GDBusProxy *proxy;
+ DBusMessageIter iter;
+ const char *address;
+
+
+ if (argc < 2 || !strlen(argv[1])) {
+ if (check_default_ctrl() == FALSE)
+ return;
+
+ proxy = default_ctrl->proxy;
+ } else {
+ adapter = find_ctrl_by_address(ctrl_list, argv[1]);
+ if (!adapter) {
+ bt_shell_printf("Controller %s not available\n",
+ argv[1]);
+ return;
+ }
+ proxy = adapter->proxy;
+ }
+
+ if (g_dbus_proxy_get_property(proxy, "Address", &iter) == FALSE)
+ return;
+
+ dbus_message_iter_get_basic(&iter, &address);
+ bt_shell_printf("Controller %s\n", address);
+
+ print_property(proxy, "Name");
+ print_property(proxy, "Alias");
+ print_property(proxy, "Class");
+ print_property(proxy, "Powered");
+ print_property(proxy, "Discoverable");
+ print_uuids(proxy);
+ print_property(proxy, "Modalias");
+ print_property(proxy, "Discovering");
+}
+
+static void cmd_select(int argc, char *argv[])
+{
+ struct adapter *adapter;
+
+ adapter = find_ctrl_by_address(ctrl_list, argv[1]);
+ if (!adapter) {
+ bt_shell_printf("Controller %s not available\n", argv[1]);
+ return;
+ }
+
+ if (default_ctrl && default_ctrl->proxy == adapter->proxy)
+ return;
+
+ forget_mesh_devices();
+
+ default_ctrl = adapter;
+ print_adapter(adapter->proxy, NULL);
+}
+
+static void generic_callback(const DBusError *error, void *user_data)
+{
+ char *str = user_data;
+
+ if (dbus_error_is_set(error))
+ bt_shell_printf("Failed to set %s: %s\n", str, error->name);
+ else
+ bt_shell_printf("Changing %s succeeded\n", str);
+}
+
+static void cmd_power(int argc, char *argv[])
+{
+ dbus_bool_t powered;
+ char *str;
+
+ if (parse_argument_on_off(argc, argv, &powered) == FALSE)
+ return;
+
+ if (check_default_ctrl() == FALSE)
+ return;
+
+ str = g_strdup_printf("power %s", powered == TRUE ? "on" : "off");
+
+ if (g_dbus_proxy_set_property_basic(default_ctrl->proxy, "Powered",
+ DBUS_TYPE_BOOLEAN, &powered,
+ generic_callback, str, g_free) == TRUE)
+ return;
+
+ g_free(str);
+}
+
+static void append_variant(DBusMessageIter *iter, int type, void *val)
+{
+ DBusMessageIter value;
+ char sig[2] = { type, '\0' };
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, sig, &value);
+
+ dbus_message_iter_append_basic(&value, type, val);
+
+ dbus_message_iter_close_container(iter, &value);
+}
+
+static void append_array_variant(DBusMessageIter *iter, int type, void *val,
+ int n_elements)
+{
+ DBusMessageIter variant, array;
+ char type_sig[2] = { type, '\0' };
+ char array_sig[3] = { DBUS_TYPE_ARRAY, type, '\0' };
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
+ array_sig, &variant);
+
+ dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY,
+ type_sig, &array);
+
+ if (dbus_type_is_fixed(type) == TRUE) {
+ dbus_message_iter_append_fixed_array(&array, type, val,
+ n_elements);
+ } else if (type == DBUS_TYPE_STRING || type == DBUS_TYPE_OBJECT_PATH) {
+ const char ***str_array = val;
+ int i;
+
+ for (i = 0; i < n_elements; i++)
+ dbus_message_iter_append_basic(&array, type,
+ &((*str_array)[i]));
+ }
+
+ dbus_message_iter_close_container(&variant, &array);
+
+ dbus_message_iter_close_container(iter, &variant);
+}
+
+static void dict_append_entry(DBusMessageIter *dict, const char *key,
+ int type, void *val)
+{
+ DBusMessageIter entry;
+
+ if (type == DBUS_TYPE_STRING) {
+ const char *str = *((const char **) val);
+
+ if (str == NULL)
+ return;
+ }
+
+ dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
+ NULL, &entry);
+
+ dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
+
+ append_variant(&entry, type, val);
+
+ dbus_message_iter_close_container(dict, &entry);
+}
+
+static void dict_append_basic_array(DBusMessageIter *dict, int key_type,
+ const void *key, int type, void *val,
+ int n_elements)
+{
+ DBusMessageIter entry;
+
+ dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
+ NULL, &entry);
+
+ dbus_message_iter_append_basic(&entry, key_type, key);
+
+ append_array_variant(&entry, type, val, n_elements);
+
+ dbus_message_iter_close_container(dict, &entry);
+}
+
+static void dict_append_array(DBusMessageIter *dict, const char *key, int type,
+ void *val, int n_elements)
+{
+ dict_append_basic_array(dict, DBUS_TYPE_STRING, &key, type, val,
+ n_elements);
+}
+
+#define DISTANCE_VAL_INVALID 0x7FFF
+
+struct set_discovery_filter_args {
+ char *transport;
+ dbus_uint16_t rssi;
+ dbus_int16_t pathloss;
+ char **uuids;
+ size_t uuids_len;
+ dbus_bool_t duplicate;
+};
+
+static void set_discovery_filter_setup(DBusMessageIter *iter, void *user_data)
+{
+ struct set_discovery_filter_args *args = user_data;
+ DBusMessageIter dict;
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING
+ DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+ dict_append_array(&dict, "UUIDs", DBUS_TYPE_STRING, &args->uuids,
+ args->uuids_len);
+
+ if (args->pathloss != DISTANCE_VAL_INVALID)
+ dict_append_entry(&dict, "Pathloss", DBUS_TYPE_UINT16,
+ &args->pathloss);
+
+ if (args->rssi != DISTANCE_VAL_INVALID)
+ dict_append_entry(&dict, "RSSI", DBUS_TYPE_INT16, &args->rssi);
+
+ if (args->transport != NULL)
+ dict_append_entry(&dict, "Transport", DBUS_TYPE_STRING,
+ &args->transport);
+ if (args->duplicate)
+ dict_append_entry(&dict, "DuplicateData", DBUS_TYPE_BOOLEAN,
+ &args->duplicate);
+
+ dbus_message_iter_close_container(iter, &dict);
+}
+
+
+static void set_discovery_filter_reply(DBusMessage *message, void *user_data)
+{
+ DBusError error;
+
+ dbus_error_init(&error);
+ if (dbus_set_error_from_message(&error, message) == TRUE) {
+ bt_shell_printf("SetDiscoveryFilter failed: %s\n", error.name);
+ dbus_error_free(&error);
+ return;
+ }
+
+ bt_shell_printf("SetDiscoveryFilter success\n");
+}
+
+static gint filtered_scan_rssi = DISTANCE_VAL_INVALID;
+static gint filtered_scan_pathloss = DISTANCE_VAL_INVALID;
+static char **filtered_scan_uuids;
+static size_t filtered_scan_uuids_len;
+static char *filtered_scan_transport = "le";
+
+static void set_scan_filter_commit(void)
+{
+ struct set_discovery_filter_args args;
+
+ args.pathloss = filtered_scan_pathloss;
+ args.rssi = filtered_scan_rssi;
+ args.transport = filtered_scan_transport;
+ args.uuids = filtered_scan_uuids;
+ args.uuids_len = filtered_scan_uuids_len;
+ args.duplicate = TRUE;
+
+ if (check_default_ctrl() == FALSE)
+ return;
+
+ if (g_dbus_proxy_method_call(default_ctrl->proxy, "SetDiscoveryFilter",
+ set_discovery_filter_setup, set_discovery_filter_reply,
+ &args, NULL) == FALSE) {
+ bt_shell_printf("Failed to set discovery filter\n");
+ return;
+ }
+}
+
+static void set_scan_filter_uuids(char *filters[])
+{
+ g_strfreev(filtered_scan_uuids);
+ filtered_scan_uuids = NULL;
+ filtered_scan_uuids_len = 0;
+
+ if (!filters)
+ goto commit;
+
+ filtered_scan_uuids = g_strdupv(filters);
+ if (!filtered_scan_uuids) {
+ bt_shell_printf("Failed to parse input\n");
+ return;
+ }
+
+ filtered_scan_uuids_len = g_strv_length(filtered_scan_uuids);
+
+commit:
+ set_scan_filter_commit();
+}
+
+static void cmd_scan_unprovisioned(int argc, char *argv[])
+{
+ dbus_bool_t enable;
+ char *filters[] = { MESH_PROV_SVC_UUID, NULL };
+ const char *method;
+
+ if (parse_argument_on_off(argc, argv, &enable) == FALSE)
+ return;
+
+ if (check_default_ctrl() == FALSE)
+ return;
+
+ if (enable == TRUE) {
+ discover_mesh = false;
+ set_scan_filter_uuids(filters);
+ method = "StartDiscovery";
+ } else {
+ method = "StopDiscovery";
+ }
+
+ if (g_dbus_proxy_method_call(default_ctrl->proxy, method,
+ NULL, start_discovery_reply,
+ GUINT_TO_POINTER(enable), NULL) == FALSE) {
+ bt_shell_printf("Failed to %s discovery\n",
+ enable == TRUE ? "start" : "stop");
+ }
+}
+
+static void cmd_info(int argc, char *argv[])
+{
+ GDBusProxy *proxy;
+ DBusMessageIter iter;
+ const char *address;
+
+ proxy = connection.device;
+ if (!proxy)
+ return;
+
+ if (g_dbus_proxy_get_property(proxy, "Address", &iter) == FALSE)
+ return;
+
+ dbus_message_iter_get_basic(&iter, &address);
+ bt_shell_printf("Device %s\n", address);
+
+ print_property(proxy, "Name");
+ print_property(proxy, "Alias");
+ print_property(proxy, "Class");
+ print_property(proxy, "Appearance");
+ print_property(proxy, "Icon");
+ print_property(proxy, "Trusted");
+ print_property(proxy, "Blocked");
+ print_property(proxy, "Connected");
+ print_uuids(proxy);
+ print_property(proxy, "Modalias");
+ print_property(proxy, "ManufacturerData");
+ print_property(proxy, "ServiceData");
+ print_property(proxy, "RSSI");
+ print_property(proxy, "TxPower");
+}
+
+static const char *security2str(uint8_t level)
+{
+ switch (level) {
+ case 0:
+ return "low";
+ case 1:
+ return "medium";
+ case 2:
+ return "high";
+ default:
+ return "invalid";
+ }
+}
+
+static void cmd_security(int argc, char *argv[])
+{
+ uint8_t level;
+ char *end;
+
+ if (argc == 1)
+ goto done;
+
+ level = strtol(argv[1], &end, 10);
+ if (end == argv[1] || !prov_set_sec_level(level)) {
+ bt_shell_printf("Invalid security level %s\n", argv[1]);
+ return;
+ }
+
+done:
+ bt_shell_printf("Provision Security Level set to %u (%s)\n",
+ prov_get_sec_level(),
+ security2str(prov_get_sec_level()));
+}
+
+static void cmd_connect(int argc, char *argv[])
+{
+ char *filters[] = { MESH_PROXY_SVC_UUID, NULL };
+
+ if (check_default_ctrl() == FALSE)
+ return;
+
+ memset(&connection, 0, sizeof(connection));
+
+ if (argc < 2 || !strlen(argv[1])) {
+ connection.net_idx = NET_IDX_PRIMARY;
+ } else {
+ char *end;
+ connection.net_idx = strtol(argv[1], &end, 16);
+ if (end == argv[1]) {
+ connection.net_idx = NET_IDX_INVALID;
+ bt_shell_printf("Invalid network index %s\n", argv[1]);
+ return;
+ }
+
+ if (argc > 2)
+ connection.unicast = strtol(argv[2], NULL, 16);
+ }
+
+ if (discovering)
+ g_dbus_proxy_method_call(default_ctrl->proxy, "StopDiscovery",
+ NULL, NULL, NULL, NULL);
+
+ set_scan_filter_uuids(filters);
+ discover_mesh = true;
+
+ if (connection.unicast == UNASSIGNED_ADDRESS) {
+ connection.type = CONN_TYPE_NETWORK;
+ bt_shell_printf("Looking for mesh network with net index "
+ "%4.4x\n", connection.net_idx);
+ } else {
+ connection.type = CONN_TYPE_IDENTITY;
+ bt_shell_printf("Looking for node id %4.4x"
+ " on network with net index %4.4x\n",
+ connection.unicast, connection.net_idx);
+ }
+
+ if (g_dbus_proxy_method_call(default_ctrl->proxy,
+ "StartDiscovery", NULL, start_discovery_reply,
+ GUINT_TO_POINTER(TRUE), NULL) == FALSE)
+ bt_shell_printf("Failed to start mesh proxy discovery\n");
+
+ g_dbus_proxy_method_call(default_ctrl->proxy, "StartDiscovery",
+ NULL, NULL, NULL, NULL);
+
+}
+
+static void prov_disconn_reply(DBusMessage *message, void *user_data)
+{
+ struct mesh_node *node = user_data;
+ char *filters[] = { MESH_PROXY_SVC_UUID, NULL };
+ DBusError error;
+
+ dbus_error_init(&error);
+
+ if (dbus_set_error_from_message(&error, message) == TRUE) {
+ bt_shell_printf("Failed to disconnect: %s\n", error.name);
+ dbus_error_free(&error);
+ return;
+ }
+
+ set_connected_device(NULL);
+
+ set_scan_filter_uuids(filters);
+ discover_mesh = true;
+
+ connection.type = CONN_TYPE_IDENTITY;
+ connection.data_in = NULL;
+ connection.data_out = NULL;
+ connection.unicast = node_get_primary(node);
+
+ if (g_dbus_proxy_method_call(default_ctrl->proxy,
+ "StartDiscovery", NULL, start_discovery_reply,
+ GUINT_TO_POINTER(TRUE), NULL) == FALSE)
+ bt_shell_printf("Failed to start mesh proxy discovery\n");
+
+}
+
+static void disconn_reply(DBusMessage *message, void *user_data)
+{
+ GDBusProxy *proxy = user_data;
+ DBusError error;
+
+ dbus_error_init(&error);
+
+ if (dbus_set_error_from_message(&error, message) == TRUE) {
+ bt_shell_printf("Failed to disconnect: %s\n", error.name);
+ dbus_error_free(&error);
+ return;
+ }
+
+ bt_shell_printf("Successfully disconnected\n");
+
+ if (proxy != connection.device)
+ return;
+
+ set_connected_device(NULL);
+}
+
+static void cmd_disconn(int argc, char *argv[])
+{
+ if (connection.type == CONN_TYPE_PROVISION) {
+ struct mesh_node *node = node_find_by_uuid(connection.dev_uuid);
+ if (node)
+ node_free(node);
+ }
+
+ disconnect_device(disconn_reply, connection.device);
+}
+
+static void mesh_prov_done(void *user_data, int status)
+{
+ struct mesh_node *node = user_data;
+
+ if (status){
+ bt_shell_printf("Provisioning failed\n");
+ node_free(node);
+ disconnect_device(NULL, NULL);
+ return;
+ }
+
+ bt_shell_printf("Provision success. Assigned Primary Unicast %4.4x\n",
+ node_get_primary(node));
+
+ if (!prov_db_add_new_node(node))
+ bt_shell_printf("Failed to add node to provisioning DB\n");
+
+ disconnect_device(prov_disconn_reply, node);
+}
+
+static void cmd_start_prov(int argc, char *argv[])
+{
+ GDBusProxy *proxy;
+ struct mesh_device *dev;
+ struct mesh_node *node;
+ int len;
+
+ len = strlen(argv[1]);
+ if ( len > 32 || len % 2) {
+ bt_shell_printf("Incorrect UUID size %d\n", len);
+ }
+
+ disconnect_device(NULL, NULL);
+
+ memset(connection.dev_uuid, 0, 16);
+ str2hex(argv[1], len, connection.dev_uuid, len/2);
+
+ node = node_find_by_uuid(connection.dev_uuid);
+ if (!node) {
+ bt_shell_printf("Device with UUID %s not found.\n", argv[1]);
+ bt_shell_printf("Stale services? Remove device and "
+ "re-discover\n");
+ return;
+ }
+
+ /* TODO: add command to remove a node from mesh, i.e., "unprovision" */
+ if (node_is_provisioned(node)) {
+ bt_shell_printf("Already provisioned with unicast %4.4x\n",
+ node_get_primary(node));
+ return;
+ }
+
+ dev = find_device_by_uuid(default_ctrl->mesh_devices,
+ connection.dev_uuid);
+ if (!dev || !dev->proxy) {
+ bt_shell_printf("Could not find device proxy\n");
+ memset(connection.dev_uuid, 0, 16);
+ return;
+ }
+
+ proxy = dev->proxy;
+ if (discovering)
+ g_dbus_proxy_method_call(default_ctrl->proxy, "StopDiscovery",
+ NULL, NULL, NULL, NULL);
+ forget_mesh_devices();
+
+ connection.type = CONN_TYPE_PROVISION;
+
+ if (g_dbus_proxy_method_call(proxy, "Connect", NULL, connect_reply,
+ proxy, NULL) == FALSE) {
+ bt_shell_printf("Failed to connect ");
+ print_device(proxy, NULL);
+ return;
+ } else {
+ bt_shell_printf("Trying to connect ");
+ print_device(proxy, NULL);
+ }
+
+}
+
+static void cmd_print_mesh(int argc, char *argv[])
+{
+ if (!prov_db_show(mesh_prov_db_filename))
+ bt_shell_printf("Unavailable\n");
+
+}
+
+ static void cmd_print_local(int argc, char *argv[])
+{
+ if (!prov_db_show(mesh_local_config_filename))
+ bt_shell_printf("Unavailable\n");
+}
+
+static const struct bt_shell_menu main_menu = {
+ .name = "main",
+ .entries = {
+ { "list", NULL, cmd_list, "List available controllers"},
+ { "show", "[ctrl]", cmd_show, "Controller information"},
+ { "select", "<ctrl>", cmd_select, "Select default controller"},
+ { "security", "[0(low)/1(medium)/2(high)]", cmd_security,
+ "Display or change provision security level"},
+ { "info", "[dev]", cmd_info, "Device information"},
+ { "connect", "[net_idx] [dst]", cmd_connect,
+ "Connect to mesh network or node on network"},
+ { "discover-unprovisioned", "<on/off>", cmd_scan_unprovisioned,
+ "Look for devices to provision" },
+ { "provision", "<uuid>", cmd_start_prov, "Initiate provisioning"},
+ { "power", "<on/off>", cmd_power, "Set controller power" },
+ { "disconnect", "[dev]", cmd_disconn, "Disconnect device"},
+ { "mesh-info", NULL, cmd_print_mesh,
+ "Mesh networkinfo (provisioner)" },
+ { "local-info", NULL, cmd_print_local, "Local mesh node info" },
+ { } },
+};
+
+static const char *mesh_config_dir;
+
+static const struct option options[] = {
+ { "config", required_argument, 0, 'c' },
+ { 0, 0, 0, 0 }
+};
+
+static const char **optargs[] = {
+ &mesh_config_dir
+};
+
+static const char *help[] = {
+ "Read local mesh config JSON files from <directory>"
+};
+
+static const struct bt_shell_opt opt = {
+ .options = options,
+ .optno = sizeof(options) / sizeof(struct option),
+ .optstr = "c:",
+ .optarg = optargs,
+ .help = help,
+};
+
+static void client_ready(GDBusClient *client, void *user_data)
+{
+ bt_shell_attach(fileno(stdin));
+}
+
+int main(int argc, char *argv[])
+{
+ GDBusClient *client;
+ int len;
+ int extra;
+
+ bt_shell_init(argc, argv, &opt);
+ bt_shell_set_menu(&main_menu);
+ bt_shell_set_prompt(PROMPT_OFF);
+
+ if (!mesh_config_dir) {
+ bt_shell_printf("Local config directory not provided.\n");
+ mesh_config_dir = "";
+ } else {
+ bt_shell_printf("Reading prov_db.json and local_node.json from %s\n",
+ mesh_config_dir);
+ }
+
+ len = strlen(mesh_config_dir);
+ if (len && mesh_config_dir[len - 1] != '/') {
+ extra = 1;
+ bt_shell_printf("mesh_config_dir[%d] %s\n", len,
+ &mesh_config_dir[len - 1]);
+ } else {
+ extra = 0;
+ }
+ mesh_local_config_filename = g_malloc(len + strlen("local_node.json")
+ + 2);
+ if (!mesh_local_config_filename)
+ exit(1);
+
+ mesh_prov_db_filename = g_malloc(len + strlen("prov_db.json") + 2);
+ if (!mesh_prov_db_filename) {
+ exit(1);
+ }
+
+ sprintf(mesh_local_config_filename, "%s", mesh_config_dir);
+
+ if (extra)
+ sprintf(mesh_local_config_filename + len , "%c", '/');
+
+ sprintf(mesh_local_config_filename + len + extra, "%s",
+ "local_node.json");
+ len = len + extra + strlen("local_node.json");
+ sprintf(mesh_local_config_filename + len, "%c", '\0');
+
+ if (!prov_db_read_local_node(mesh_local_config_filename, true)) {
+ g_printerr("Failed to parse local node configuration file %s\n",
+ mesh_local_config_filename);
+ exit(1);
+ }
+
+ sprintf(mesh_prov_db_filename, "%s", mesh_config_dir);
+ len = strlen(mesh_config_dir);
+ if (extra)
+ sprintf(mesh_prov_db_filename + len , "%c", '/');
+
+ sprintf(mesh_prov_db_filename + len + extra, "%s", "prov_db.json");
+ sprintf(mesh_prov_db_filename + len + extra + strlen("prov_db.json"),
+ "%c", '\0');
+
+ if (!prov_db_read(mesh_prov_db_filename)) {
+ g_printerr("Failed to parse provisioning database file %s\n",
+ mesh_prov_db_filename);
+ exit(1);
+ }
+
+ dbus_conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, NULL);
+ client = g_dbus_client_new(dbus_conn, "org.bluez", "/org/bluez");
+
+ g_dbus_client_set_connect_watch(client, connect_handler, NULL);
+ g_dbus_client_set_disconnect_watch(client, disconnect_handler, NULL);
+ g_dbus_client_set_signal_watch(client, message_handler, NULL);
+
+ g_dbus_client_set_proxy_handlers(client, proxy_added, proxy_removed,
+ property_changed, NULL);
+
+ g_dbus_client_set_ready_watch(client, client_ready, NULL);
+
+ if (!config_client_init())
+ g_printerr("Failed to initialize mesh configuration client\n");
+
+ if (!config_server_init())
+ g_printerr("Failed to initialize mesh configuration server\n");
+
+ if (!onoff_client_init(PRIMARY_ELEMENT_IDX))
+ g_printerr("Failed to initialize mesh generic On/Off client\n");
+
+ bt_shell_run();
+
+ g_dbus_client_unref(client);
+
+ dbus_connection_unref(dbus_conn);
+
+ node_cleanup();
+
+ g_list_free(char_list);
+ g_list_free(service_list);
+ g_list_free_full(ctrl_list, proxy_leak);
+
+ return 0;
+}
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2017 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
+ *
+ */
+
+/* Proxy PDU Types */
+#define PROXY_NETWORK_PDU 0x00
+#define PROXY_MESH_BEACON 0x01
+#define PROXY_CONFIG_PDU 0x02
+#define PROXY_PROVISIONING_PDU 0x03
+
+#define CTL 0x80
+#define TTL_MASK 0x7f
+#define SEQ_MASK 0xffffff
+
+#define CREDFLAG_MASK 0x1000
+#define APP_IDX_MASK 0x0fff
+#define APP_IDX_DEV 0x7fff
+#define APP_IDX_ANY 0x8000
+#define APP_IDX_NET 0xffff
+#define APP_IDX_INVALID 0xffff
+
+#define NET_IDX_INVALID 0xffff
+#define NET_IDX_PRIMARY 0x0000
+
+#define KEY_CACHE_SIZE 64
+#define FRND_CACHE_MAX 32
+
+#define UNASSIGNED_ADDRESS 0x0000
+#define PROXIES_ADDRESS 0xfffc
+#define FRIENDS_ADDRESS 0xfffd
+#define RELAYS_ADDRESS 0xfffe
+#define ALL_NODES_ADDRESS 0xffff
+#define VIRTUAL_ADDRESS_LOW 0x8000
+#define VIRTUAL_ADDRESS_HIGH 0xbfff
+#define GROUP_ADDRESS_LOW 0xc000
+#define GROUP_ADDRESS_HIGH 0xff00
+
+#define DEFAULT_TTL 0xff
+
+#define PRIMARY_ELEMENT_IDX 0x00
+
+#define MAX_UNSEG_LEN 15 /* msg_len == 11 + sizeof(MIC) */
+#define MAX_SEG_LEN 12 /* UnSeg length - 3 octets overhead */
+#define SEG_MAX(len) (((len) <= MAX_UNSEG_LEN) ? 0 : \
+ (((len) - 1) / MAX_SEG_LEN))
+#define SEG_OFF(seg) ((seg) * MAX_SEG_LEN)
+#define MAX_SEG_TO_LEN(seg) ((seg) ? SEG_OFF((seg) + 1) : MAX_UNSEG_LEN)
+
+
+#define IS_UNASSIGNED(x) ((x) == UNASSIGNED_ADDRESS)
+#define IS_UNICAST(x) (((x) > UNASSIGNED_ADDRESS) && \
+ ((x) < VIRTUAL_ADDRESS_LOW))
+#define IS_VIRTUAL(x) (((x) >= VIRTUAL_ADDRESS_LOW) && \
+ ((x) <= VIRTUAL_ADDRESS_HIGH))
+#define IS_GROUP(x) (((x) >= GROUP_ADDRESS_LOW) && \
+ ((x) <= GROUP_ADDRESS_HIGH))
+#define IS_ALL_NODES(x) ((x) == ALL_NODES_ADDRESS)
+
+#define SEGMENTED 0x80
+#define UNSEGMENTED 0x00
+#define SEG_HDR_SHIFT 31
+#define IS_SEGMENTED(hdr) (!!((hdr) & (true << SEG_HDR_SHIFT)))
+
+#define KEY_ID_MASK 0x7f
+#define KEY_AID_MASK 0x3f
+#define KEY_ID_AKF 0x40
+#define KEY_AID_SHIFT 0
+#define AKF_HDR_SHIFT 30
+#define KEY_HDR_SHIFT 24
+#define HAS_APP_KEY(hdr) (!!((hdr) & (true << AKF_HDR_SHIFT)))
+
+#define OPCODE_MASK 0x7f
+#define OPCODE_HDR_SHIFT 24
+#define RELAY 0x80
+#define RELAY_HDR_SHIFT 23
+#define SZMIC 0x80
+#define SZMIC_HDR_SHIFT 23
+#define SEQ_ZERO_MASK 0x1fff
+#define SEQ_ZERO_HDR_SHIFT 10
+#define IS_RELAYED(hdr) (!!((hdr) & (true << RELAY_HDR_SHIFT)))
+#define HAS_MIC64(hdr) (!!((hdr) & (true << SZMIC_HDR_SHIFT)))
+
+#define SEG_MASK 0x1f
+#define SEGO_HDR_SHIFT 5
+#define SEGN_HDR_SHIFT 0
+#define SEG_TOTAL(hdr) (((hdr) >> SEGN_HDR_SHIFT) & SEG_MASK)
+/* Proxy Configuration Opcodes */
+#define PROXY_OP_SET_FILTER_TYPE 0x00
+#define PROXY_OP_FILTER_ADD 0x01
+#define PROXY_OP_FILTER_DEL 0x02
+#define PROXY_OP_FILTER_STATUS 0x03
+
+/* Proxy Filter Defines */
+#define PROXY_FILTER_WHITELIST 0x00
+#define PROXY_FILTER_BLACKLIST 0x01
+
+/* Network Tranport Opcodes */
+#define NET_OP_SEG_ACKNOWLEDGE 0x00
+#define NET_OP_FRND_POLL 0x01
+#define NET_OP_FRND_UPDATE 0x02
+#define NET_OP_FRND_REQUEST 0x03
+#define NET_OP_FRND_OFFER 0x04
+#define NET_OP_FRND_CLEAR 0x05
+#define NET_OP_FRND_CLEAR_CONFIRM 0x06
+
+#define NET_OP_PROXY_SUB_ADD 0x07
+#define NET_OP_PROXY_SUB_REMOVE 0x08
+#define NET_OP_PROXY_SUB_CONFIRM 0x09
+#define NET_OP_HEARTBEAT 0x0a
+
+/* Key refresh state on the mesh */
+#define NET_KEY_REFRESH_PHASE_NONE 0x00
+#define NET_KEY_REFRESH_PHASE_ONE 0x01
+#define NET_KEY_REFRESH_PHASE_TWO 0x02
+#define NET_KEY_REFRESH_PHASE_THREE 0x03
+
+#define MESH_FEATURE_RELAY 1
+#define MESH_FEATURE_PROXY 2
+#define MESH_FEATURE_FRIEND 4
+#define MESH_FEATURE_LPN 8
+
+#define MESH_MAX_ACCESS_PAYLOAD 380
+
+#define MESH_STATUS_SUCCESS 0x00
+#define MESH_STATUS_INVALID_ADDRESS 0x01
+#define MESH_STATUS_INVALID_MODEL 0x02
+#define MESH_STATUS_INVALID_APPKEY 0x03
+#define MESH_STATUS_INVALID_NETKEY 0x04
+#define MESH_STATUS_INSUFF_RESOURCES 0x05
+#define MESH_STATUS_IDX_ALREADY_STORED 0x06
+#define MESH_STATUS_INVALID_PUB_PARAM 0x07
+#define MESH_STATUS_NOT_SUB_MOD 0x08
+#define MESH_STATUS_STORAGE_FAIL 0x09
+#define MESH_STATUS_FEAT_NOT_SUP 0x0a
+#define MESH_STATUS_CANNOT_UPDATE 0x0b
+#define MESH_STATUS_CANNOT_REMOVE 0x0c
+#define MESH_STATUS_CANNOT_BIND 0x0d
+#define MESH_STATUS_UNABLE_CHANGE_STATE 0x0e
+#define MESH_STATUS_CANNOT_SET 0x0f
+#define MESH_STATUS_UNSPECIFIED_ERROR 0x10
+#define MESH_STATUS_INVALID_BINDING 0x11
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2017 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 <inttypes.h>
+#include <ctype.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <glib.h>
+
+#include "src/shared/util.h"
+#include "src/shared/shell.h"
+
+#include "mesh/crypto.h"
+#include "mesh/gatt.h"
+#include "mesh/mesh-net.h"
+#include "mesh/util.h"
+#include "mesh/keys.h"
+#include "mesh/node.h"
+#include "mesh/prov-db.h"
+#include "mesh/net.h"
+
+struct address_range
+{
+ uint16_t min;
+ uint16_t max;
+};
+
+struct mesh_net {
+ uint32_t iv_index;
+ uint32_t seq_num;
+ uint32_t seq_num_reserved;
+ uint16_t primary_addr;
+ uint8_t iv_upd_state;
+ uint8_t num_elements;
+ uint8_t default_ttl;
+ bool iv_update;
+ bool provisioner;
+ bool blacklist;
+ guint iv_update_timeout;
+ GDBusProxy *proxy_in;
+ GList *address_pool;
+ GList *dest; /* List of valid local destinations for Whitelist */
+ GList *sar_in; /* Incoming segmented messages in progress */
+ GList *msg_out; /* Pre-Network encoded, might be multi-segment */
+ GList *pkt_out; /* Fully encoded packets awaiting Tx in order */
+ net_mesh_session_open_callback open_cb;
+};
+
+struct generic_key {
+ uint16_t idx;
+};
+
+struct net_key_parts {
+ uint8_t nid;
+ uint8_t enc_key[16];
+ uint8_t privacy_key[16];
+ uint8_t net_key[16];
+ uint8_t beacon_key[16];
+ uint8_t net_id[8];
+};
+
+struct mesh_net_key {
+ struct generic_key generic;
+ uint8_t phase;
+ struct net_key_parts current;
+ struct net_key_parts new;
+};
+
+struct app_key_parts {
+ uint8_t key[16];
+ uint8_t akf_aid;
+};
+
+struct mesh_app_key {
+ struct generic_key generic;
+ uint16_t net_idx;
+ struct app_key_parts current;
+ struct app_key_parts new;
+};
+
+struct mesh_virt_addr {
+ uint16_t va16;
+ uint32_t va32;
+ uint8_t va128[16];
+};
+
+struct mesh_pkt {
+ uint8_t data[30];
+ uint8_t len;
+};
+
+struct mesh_sar_msg {
+ guint ack_to;
+ guint msg_to;
+ uint32_t iv_index;
+ uint32_t seqAuth;
+ uint32_t ack;
+ uint32_t dst;
+ uint16_t src;
+ uint16_t net_idx;
+ uint16_t len;
+ uint8_t akf_aid;
+ uint8_t ttl;
+ uint8_t segN;
+ uint8_t activity_cnt;
+ bool ctl;
+ bool segmented;
+ bool szmic;
+ bool proxy;
+ uint8_t data[20]; /* Open ended, min 20 */
+};
+
+struct mesh_destination {
+ uint16_t cnt;
+ uint16_t dst;
+};
+
+/* Network Packet Layer based Offsets */
+#define AKF_BIT 0x40
+
+#define PKT_IVI(p) !!((p)[0] & 0x80)
+#define SET_PKT_IVI(p,v) do {(p)[0] &= 0x7f; \
+ (p)[0] |= ((v) ? 0x80 : 0);} while(0)
+#define PKT_NID(p) ((p)[0] & 0x7f)
+#define SET_PKT_NID(p,v) do {(p)[0] &= 0x80; (p)[0] |= (v);} while(0)
+#define PKT_CTL(p) (!!((p)[1] & 0x80))
+#define SET_PKT_CTL(p,v) do {(p)[1] &= 0x7f; \
+ (p)[1] |= ((v) ? 0x80 : 0);} while(0)
+#define PKT_TTL(p) ((p)[1] & 0x7f)
+#define SET_PKT_TTL(p,v) do {(p)[1] &= 0x80; (p)[1] |= (v);} while(0)
+#define PKT_SEQ(p) (get_be32((p) + 1) & 0xffffff)
+#define SET_PKT_SEQ(p,v) put_be32(((p)[1] << 24) + ((v) & 0xffffff), \
+ (p) + 1)
+#define PKT_SRC(p) get_be16((p) + 5)
+#define SET_PKT_SRC(p,v) put_be16(v, (p) + 5)
+#define PKT_DST(p) get_be16((p) + 7)
+#define SET_PKT_DST(p,v) put_be16(v, (p) + 7)
+#define PKT_TRANS(p) ((p) + 9)
+#define PKT_TRANS_LEN(l) ((l) - 9)
+
+#define PKT_SEGMENTED(p) (!!((p)[9] & 0x80))
+#define SET_PKT_SEGMENTED(p,v) do {(p)[9] &= 0x7f; \
+ (p)[9] |= ((v) ? 0x80 : 0);} while(0)
+#define PKT_AKF_AID(p) ((p)[9] & 0x7f)
+#define SET_PKT_AKF_AID(p,v) do {(p)[9] &= 0x80; (p)[9] |= (v);} while(0)
+#define PKT_OPCODE(p) ((p)[9] & 0x7f)
+#define SET_PKT_OPCODE(p,v) do {(p)[9] &= 0x80; (p)[9] |= (v);} while(0)
+#define PKT_OBO(p) (!!((p)[10] & 0x80))
+#define PKT_SZMIC(p) (!!(PKT_SEGMENTED(p) ? ((p)[10] & 0x40) : 0))
+#define SET_PKT_SZMIC(p,v) do {(p)[10] &= 0x7f; \
+ (p)[10] |= ((v) ? 0x80 : 0);} while(0)
+#define PKT_SEQ0(p) ((get_be16((p) + 10) >> 2) & 0x1fff)
+#define SET_PKT_SEQ0(p,v) do {put_be16((get_be16((p) + 10) & 0x8003) \
+ | (((v) & 0x1fff) << 2), \
+ (p) + 10);} while(0)
+#define SET_PKT_SEGO(p,v) do {put_be16((get_be16( \
+ (p) + 11) & 0xfc1f) | ((v) << 5), \
+ (p) + 11);} while(0)
+#define SET_PKT_SEGN(p,v) do {(p)[12] = ((p)[12] & 0xe0) | (v);} while(0)
+#define PKT_ACK(p) (get_be32((p) + 12))
+#define SET_PKT_ACK(p,v) (put_be32((v)(p) + 12))
+
+/* Transport Layer based offsets */
+#define TRANS_SEGMENTED(t) (!!((t)[0] & 0x80))
+#define SET_TRANS_SEGMENTD(t,v) do {(t)[0] &= 0x7f; \
+ (t)[0] |= ((v) ? 0x80 : 0);} while(0)
+#define TRANS_OPCODE(t) ((t)[0] & 0x7f)
+#define SET_TRANS_OPCODE(t,v) do {(t)[0] &= 0x80; (t)[0] |= (v);} while(0)
+#define TRANS_AKF_AID(t) ((t)[0] & 0x7f)
+#define SET_TRANS_AKF_AID(t,v) do {(t)[0] &= 0xc0; (t)[0] |= (v);} while(0)
+#define TRANS_AKF(t) (!!((t)[0] & AKF_BIT))
+#define TRANS_SZMIC(t) (!!(TRANS_SEGMENTED(t) ? ((t)[1] & 0x80) : 0))
+#define TRANS_SEQ0(t) ((get_be16((t) + 1) >> 2) & 0x1fff)
+#define SET_TRANS_SEQ0(t,v) do {put_be16((get_be16((t) + 1) & 0x8003) \
+ | (((v) & 0x1fff) << 2), \
+ (t) + 1);} while(0)
+#define SET_TRANS_ACK(t,v) put_be32((v), (t) + 3)
+#define TRANS_SEGO(t) ((get_be16((t) + 2) >> 5) & 0x1f)
+#define TRANS_SEGN(t) ((t)[3] & 0x1f)
+
+#define TRANS_PAYLOAD(t) ((t) + (TRANS_SEGMENTED(t) ? 4 : 1))
+#define TRANS_LEN(t,l) ((l) -(TRANS_SEGMENTED(t) ? 4 : 1))
+
+/* Proxy Config Opcodes */
+#define FILTER_SETUP 0x00
+#define FILTER_ADD 0x01
+#define FILTER_DEL 0x02
+#define FILTER_STATUS 0x03
+
+/* Proxy Filter Types */
+#define WHITELIST_FILTER 0x00
+#define BLACKLIST_FILTER 0x01
+
+/* IV Updating states for timing enforcement */
+#define IV_UPD_INIT 0
+#define IV_UPD_NORMAL 1
+#define IV_UPD_UPDATING 2
+#define IV_UPD_NORMAL_HOLD 3
+
+#define IV_IDX_DIFF_RANGE 42
+
+static struct mesh_net net;
+static GList *virt_addrs = NULL;
+static GList *net_keys = NULL;
+static GList *app_keys = NULL;
+
+/* Forward static declarations */
+static void resend_segs(struct mesh_sar_msg *sar);
+
+static int match_net_id(const void *a, const void *net_id)
+{
+ const struct mesh_net_key *net_key = a;
+
+ if (net_key->current.nid != 0xff &&
+ !memcmp(net_key->current.net_id, net_id, 8))
+ return 0;
+
+ if (net_key->new.nid != 0xff &&
+ !memcmp(net_key->new.net_id, net_id, 8))
+ return 0;
+
+ return -1;
+}
+
+static struct mesh_net_key *find_net_key_by_id(const uint8_t *net_id)
+{
+ GList *l;
+
+ l = g_list_find_custom(net_keys, net_id, match_net_id);
+
+ if (!l)
+ return NULL;
+
+ return l->data;
+}
+
+uint16_t net_validate_proxy_beacon(const uint8_t *proxy_beacon)
+{
+ struct mesh_net_key *net_key = find_net_key_by_id(proxy_beacon);
+
+ if (net_key == NULL)
+ return NET_IDX_INVALID;
+
+ return net_key->generic.idx;
+}
+
+static int match_sar_dst(const void *a, const void *b)
+{
+ const struct mesh_sar_msg *sar = a;
+ uint16_t dst = GPOINTER_TO_UINT(b);
+
+ return (sar->dst == dst) ? 0 : -1;
+}
+
+static struct mesh_sar_msg *find_sar_out_by_dst(uint16_t dst)
+{
+ GList *l;
+
+ l = g_list_find_custom(net.msg_out, GUINT_TO_POINTER(dst),
+ match_sar_dst);
+
+ if (!l)
+ return NULL;
+
+ return l->data;
+}
+
+static int match_sar_src(const void *a, const void *b)
+{
+ const struct mesh_sar_msg *sar = a;
+ uint16_t src = GPOINTER_TO_UINT(b);
+
+ return (sar->src == src) ? 0 : -1;
+}
+
+static struct mesh_sar_msg *find_sar_in_by_src(uint16_t src)
+{
+ GList *l;
+
+ l = g_list_find_custom(net.sar_in, GUINT_TO_POINTER(src),
+ match_sar_src);
+
+ if (!l)
+ return NULL;
+
+ return l->data;
+}
+
+static int match_key_index(const void *a, const void *b)
+{
+ const struct generic_key *generic = a;
+ uint16_t index = GPOINTER_TO_UINT(b);
+
+ return (generic->idx == index) ? 0 : -1;
+}
+
+static bool delete_key(GList **list, uint16_t index)
+{
+ GList *l;
+
+ l = g_list_find_custom(*list, GUINT_TO_POINTER(index),
+ match_key_index);
+
+ if (!l)
+ return false;
+
+ *list = g_list_delete_link(*list, l);
+
+ return true;
+
+}
+
+static uint8_t *get_key(GList *list, uint16_t index)
+{
+ GList *l;
+ struct mesh_app_key *app_key;
+ struct mesh_net_key *net_key;
+
+ l = g_list_find_custom(list, GUINT_TO_POINTER(index),
+ match_key_index);
+
+ if (!l) return NULL;
+
+ if (list == app_keys) {
+ app_key = l->data;
+
+ /* All App Keys must belong to a valid Net Key */
+ l = g_list_find_custom(net_keys,
+ GUINT_TO_POINTER(app_key->net_idx),
+ match_key_index);
+
+ if (!l) return NULL;
+
+ net_key = l->data;
+
+ if (net_key->phase == 2 && app_key->new.akf_aid != 0xff)
+ return app_key->new.key;
+
+ if (app_key->current.akf_aid != 0xff)
+ return app_key->current.key;
+
+ return NULL;
+ }
+
+ net_key = l->data;
+
+ if (net_key->phase == 2 && net_key->new.nid != 0xff)
+ return net_key->new.net_key;
+
+ if (net_key->current.nid != 0xff)
+ return net_key->current.net_key;
+
+ return NULL;
+}
+
+bool keys_app_key_add(uint16_t net_idx, uint16_t app_idx, uint8_t *key,
+ bool update)
+{
+ struct mesh_app_key *app_key = NULL;
+ uint8_t akf_aid;
+ GList *l = g_list_find_custom(app_keys, GUINT_TO_POINTER(app_idx),
+ match_key_index);
+
+ if (!mesh_crypto_k4(key, &akf_aid))
+ return false;
+
+ akf_aid |= AKF_BIT;
+
+ if (l && update) {
+
+ app_key = l->data;
+
+ if (app_key->net_idx != net_idx)
+ return false;
+
+ memcpy(app_key->new.key, key, 16);
+ app_key->new.akf_aid = akf_aid;
+
+ } else if (l) {
+
+ app_key = l->data;
+
+ if (memcmp(app_key->current.key, key, 16) ||
+ app_key->net_idx != net_idx)
+ return false;
+
+ } else {
+
+ app_key = g_new(struct mesh_app_key, 1);
+ memcpy(app_key->current.key, key, 16);
+ app_key->net_idx = net_idx;
+ app_key->generic.idx = app_idx;
+ app_key->current.akf_aid = akf_aid;
+
+ /* Invalidate "New" version */
+ app_key->new.akf_aid = 0xff;
+
+ app_keys = g_list_append(app_keys, app_key);
+
+ }
+
+ return true;
+}
+
+bool keys_net_key_add(uint16_t net_idx, uint8_t *key, bool update)
+{
+ struct mesh_net_key *net_key = NULL;
+ uint8_t p = 0;
+ GList *l = g_list_find_custom(net_keys, GUINT_TO_POINTER(net_idx),
+ match_key_index);
+
+ if (l && update) {
+ bool result;
+
+ net_key = l->data;
+
+ memcpy(net_key->new.net_key, key, 16);
+
+ /* Calculate the many component parts */
+ result = mesh_crypto_nkbk(key, net_key->new.beacon_key);
+ if (!result)
+ return false;
+
+ result = mesh_crypto_k3(key, net_key->new.net_id);
+ if (!result)
+ return false;
+
+ result = mesh_crypto_k2(key, &p, 1,
+ &net_key->new.nid,
+ net_key->new.enc_key,
+ net_key->new.privacy_key);
+ if (!result)
+ net_key->new.nid = 0xff;
+
+ return result;
+
+ } else if (l) {
+ net_key = l->data;
+
+ if (memcmp(net_key->current.net_key, key, 16))
+ return false;
+ } else {
+ bool result;
+
+ net_key = g_new(struct mesh_net_key, 1);
+ memcpy(net_key->current.net_key, key, 16);
+ net_key->generic.idx = net_idx;
+
+ /* Invalidate "New" version */
+ net_key->new.nid = 0xff;
+
+ /* Calculate the many component parts */
+ result = mesh_crypto_nkbk(key, net_key->current.beacon_key);
+ if (!result) {
+ g_free(net_key);
+ return false;
+ }
+
+ result = mesh_crypto_k3(key, net_key->current.net_id);
+ if (!result) {
+ g_free(net_key);
+ return false;
+ }
+
+ result = mesh_crypto_k2(key, &p, 1,
+ &net_key->current.nid,
+ net_key->current.enc_key,
+ net_key->current.privacy_key);
+
+ if (!result) {
+ g_free(net_key);
+ return false;
+ }
+
+ net_keys = g_list_append(net_keys, net_key);
+ }
+
+ return true;
+}
+
+static struct mesh_app_key *find_app_key_by_idx(uint16_t app_idx)
+{
+ GList *l;
+
+ l = g_list_find_custom(app_keys, GUINT_TO_POINTER(app_idx),
+ match_key_index);
+
+ if (!l) return NULL;
+
+ return l->data;
+}
+
+static struct mesh_net_key *find_net_key_by_idx(uint16_t net_idx)
+{
+ GList *l;
+
+ l = g_list_find_custom(net_keys, GUINT_TO_POINTER(net_idx),
+ match_key_index);
+
+ if (!l) return NULL;
+
+ return l->data;
+}
+
+static int match_virt_dst(const void *a, const void *b)
+{
+ const struct mesh_virt_addr *virt = a;
+ uint32_t dst = GPOINTER_TO_UINT(b);
+
+ if (dst < 0x10000 && dst == virt->va16)
+ return 0;
+
+ if (dst == virt->va32)
+ return 0;
+
+ return -1;
+}
+
+static struct mesh_virt_addr *find_virt_by_dst(uint32_t dst)
+{
+ GList *l;
+
+ l = g_list_find_custom(virt_addrs, GUINT_TO_POINTER(dst),
+ match_virt_dst);
+
+ if (!l) return NULL;
+
+ return l->data;
+}
+
+uint8_t *keys_net_key_get(uint16_t net_idx, bool current)
+{
+ GList *l;
+
+
+ l = g_list_find_custom(net_keys, GUINT_TO_POINTER(net_idx),
+ match_key_index);
+ if (!l) {
+ return NULL;
+ } else {
+ struct mesh_net_key *key = l->data;
+ if (current)
+ return key->current.net_key;
+ else
+ return key->new.net_key;
+ }
+}
+
+bool keys_app_key_delete(uint16_t app_idx)
+{
+ /* TODO: remove all associated bindings */
+ return delete_key(&app_keys, app_idx);
+}
+
+bool keys_net_key_delete(uint16_t net_idx)
+{
+ /* TODO: remove all associated app keys and bindings */
+ return delete_key(&net_keys, net_idx);
+}
+
+uint8_t keys_get_kr_phase(uint16_t net_idx)
+{
+ GList *l;
+ struct mesh_net_key *key;
+
+ l = g_list_find_custom(net_keys, GUINT_TO_POINTER(net_idx),
+ match_key_index);
+
+ if (!l)
+ return KR_PHASE_INVALID;
+
+ key = l->data;
+
+ return key->phase;
+}
+
+bool keys_set_kr_phase(uint16_t index, uint8_t phase)
+{
+ GList *l;
+ struct mesh_net_key *net_key;
+
+ l = g_list_find_custom(net_keys, GUINT_TO_POINTER(index),
+ match_key_index);
+
+ if (!l)
+ return false;
+
+ net_key = l->data;
+ net_key->phase = phase;
+
+ return true;
+}
+
+uint16_t keys_app_key_get_bound(uint16_t app_idx)
+{
+ GList *l;
+
+ l = g_list_find_custom(app_keys, GUINT_TO_POINTER(app_idx),
+ match_key_index);
+ if (!l)
+ return NET_IDX_INVALID;
+ else {
+ struct mesh_app_key *key = l->data;
+ return key->net_idx;
+ }
+}
+
+uint8_t *keys_app_key_get(uint16_t app_idx, bool current)
+{
+ GList *l;
+
+
+ l = g_list_find_custom(app_keys, GUINT_TO_POINTER(app_idx),
+ match_key_index);
+ if (!l) {
+ return NULL;
+ } else {
+ struct mesh_app_key *key = l->data;
+ if (current)
+ return key->current.key;
+ else
+ return key->new.key;
+ }
+}
+
+void keys_cleanup_all(void)
+{
+ g_list_free_full(app_keys, g_free);
+ g_list_free_full(net_keys, g_free);
+ app_keys = net_keys = NULL;
+}
+
+bool net_get_key(uint16_t net_idx, uint8_t *key)
+{
+ uint8_t *buf;
+
+ buf = get_key(net_keys, net_idx);
+
+ if (!buf)
+ return false;
+
+ memcpy(key, buf, 16);
+ return true;
+}
+
+bool net_get_flags(uint16_t net_idx, uint8_t *out_flags)
+{
+ uint8_t phase;
+
+ phase = keys_get_kr_phase(net_idx);
+
+ if (phase == KR_PHASE_INVALID || !out_flags)
+ return false;
+
+ if (phase != KR_PHASE_NONE)
+ *out_flags = 0x01;
+ else
+ *out_flags = 0x00;
+
+ if (net.iv_update)
+ *out_flags |= 0x02;
+
+ return true;
+}
+
+uint32_t net_get_iv_index(bool *update)
+{
+ if (update)
+ *update = net.iv_update;
+
+ return net.iv_index;
+}
+
+void net_set_iv_index(uint32_t iv_index, bool update)
+{
+ net.iv_index = iv_index;
+ net.iv_update = update;
+}
+
+void set_sequence_number(uint32_t seq_num)
+{
+ net.seq_num = seq_num;
+}
+
+uint32_t get_sequence_number(void)
+{
+ return net.seq_num;
+}
+
+bool net_add_address_pool(uint16_t min, uint16_t max)
+{
+ uint32_t range;
+ if (max < min)
+ return false;
+ range = min + (max << 16);
+ net.address_pool = g_list_append(net.address_pool,
+ GUINT_TO_POINTER(range));
+ return true;
+}
+
+static int match_address_range(const void *a, const void *b)
+{
+ uint32_t range = GPOINTER_TO_UINT(a);
+ uint8_t num_elements = (uint8_t) (GPOINTER_TO_UINT(b));
+ uint16_t max = range >> 16;
+ uint16_t min = range & 0xffff;
+
+ return ((max - min) >= (num_elements - 1)) ? 0 : -1;
+
+}
+
+uint16_t net_obtain_address(uint8_t num_eles)
+{
+ uint16_t addr;
+ GList *l;
+
+ l = g_list_find_custom(net.address_pool, GUINT_TO_POINTER(num_eles),
+ match_address_range);
+ if (l) {
+ uint32_t range = GPOINTER_TO_UINT(l->data);
+ uint16_t max = range >> 16;
+ uint16_t min = range & 0xffff;
+
+ addr = min;
+ min += num_eles;
+
+ if (min > max)
+ net.address_pool = g_list_delete_link(net.address_pool,
+ l);
+ else {
+ range = min + (max << 16);
+ l->data = GUINT_TO_POINTER(range);
+ }
+ return addr;
+ }
+
+ return UNASSIGNED_ADDRESS;
+}
+
+static int range_cmp(const void *a, const void *b)
+{
+ uint32_t range1 = GPOINTER_TO_UINT(a);
+ uint32_t range2 = GPOINTER_TO_UINT(b);
+
+ return range2 - range1;
+}
+
+void net_release_address(uint16_t addr, uint8_t num_elements)
+{
+ GList *l;
+ uint32_t range;
+
+ for (l = net.address_pool; l != NULL; l = l->next)
+ {
+ uint16_t max;
+ uint16_t min;
+
+ range = GPOINTER_TO_UINT(l->data);
+
+ max = range >> 16;
+ min = range & 0xffff;
+
+ if (min == (addr + num_elements + 1))
+ min = addr;
+ else if (addr && max == (addr - 1))
+ max = addr + num_elements + 1;
+ else
+ continue;
+
+ range = min + (max << 16);
+ l->data = GUINT_TO_POINTER(range);
+ return;
+ }
+
+ range = addr + ((addr + num_elements - 1) << 16);
+ net.address_pool = g_list_insert_sorted(net.address_pool,
+ GUINT_TO_POINTER(range),
+ range_cmp);
+}
+
+bool net_reserve_address_range(uint16_t base, uint8_t num_elements)
+{
+ GList *l;
+ uint32_t range;
+ uint16_t max;
+ uint16_t min;
+ bool shrink;
+
+ for (l = net.address_pool; l != NULL; l = l->next) {
+
+ range = GPOINTER_TO_UINT(l->data);
+
+ max = range >> 16;
+ min = range & 0xffff;
+
+ if (base >= min && (base + num_elements - 1) <= max)
+ break;
+ }
+
+ if (!l)
+ return false;
+
+ net.address_pool = g_list_delete_link(net.address_pool, l);
+
+ shrink = false;
+
+ if (base == min) {
+ shrink = true;
+ min = base + num_elements;
+ }
+
+ if (max == base + num_elements - 1) {
+ shrink = true;
+ max -= num_elements;
+ }
+
+ if (min > max)
+ return true;
+
+ if (shrink)
+ range = min + (max << 16);
+ else
+ range = min + ((base - 1) << 16);
+
+ net.address_pool = g_list_insert_sorted(net.address_pool,
+ GUINT_TO_POINTER(range),
+ range_cmp);
+
+ if (shrink)
+ return true;
+
+ range = (base + num_elements) + (max << 16);
+ net.address_pool = g_list_insert_sorted(net.address_pool,
+ GUINT_TO_POINTER(range),
+ range_cmp);
+
+ return true;
+}
+
+static int match_destination(const void *a, const void *b)
+{
+ const struct mesh_destination *dest = a;
+ uint16_t dst = GPOINTER_TO_UINT(b);
+
+ return (dest->dst == dst) ? 0 : -1;
+}
+
+void net_dest_ref(uint16_t dst)
+{
+ struct mesh_destination *dest;
+ GList *l;
+
+ if (!dst) return;
+
+ l = g_list_find_custom(net.dest, GUINT_TO_POINTER(dst),
+ match_destination);
+
+ if (l) {
+ dest = l->data;
+ dest->cnt++;
+ return;
+ }
+
+ dest = g_new0(struct mesh_destination, 1);
+ dest->dst = dst;
+ dest->cnt++;
+ net.dest = g_list_append(net.dest, dest);
+}
+
+void net_dest_unref(uint16_t dst)
+{
+ struct mesh_destination *dest;
+ GList *l;
+
+ l = g_list_find_custom(net.dest, GUINT_TO_POINTER(dst),
+ match_destination);
+
+ if (!l)
+ return;
+
+ dest = l->data;
+ dest->cnt--;
+
+ if (dest->cnt == 0) {
+ net.dest = g_list_remove(net.dest, dest);
+ g_free(dest);
+ }
+}
+
+struct build_whitelist {
+ uint8_t len;
+ uint8_t data[12];
+};
+
+static void whitefilter_add(gpointer data, gpointer user_data)
+{
+ struct mesh_destination *dest = data;
+ struct build_whitelist *white = user_data;
+
+ if (white->len == 0)
+ white->data[white->len++] = FILTER_ADD;
+
+ put_be16(dest->dst, white->data + white->len);
+ white->len += 2;
+
+ if (white->len > (sizeof(white->data) - sizeof(uint16_t))) {
+ net_ctl_msg_send(0, 0, 0, white->data, white->len);
+ white->len = 0;
+ }
+}
+
+static void setup_whitelist()
+{
+ struct build_whitelist white;
+
+ white.len = 0;
+
+ /* Enable (and Clear) Proxy Whitelist */
+ white.data[white.len++] = FILTER_SETUP;
+ white.data[white.len++] = WHITELIST_FILTER;
+
+ net_ctl_msg_send(0, 0, 0, white.data, white.len);
+
+ white.len = 0;
+ g_list_foreach(net.dest, whitefilter_add, &white);
+
+ if (white.len)
+ net_ctl_msg_send(0, 0, 0, white.data, white.len);
+}
+
+static void beacon_update(bool first, bool iv_update, uint32_t iv_index)
+{
+
+ /* Enforcement of 96 hour and 192 hour IVU time windows */
+ if (iv_update && !net.iv_update) {
+ bt_shell_printf("iv_upd_state = IV_UPD_UPDATING\n");
+ net.iv_upd_state = IV_UPD_UPDATING;
+ /* TODO: Start timer to enforce IV Update parameters */
+ } else if (first) {
+ if (iv_update)
+ net.iv_upd_state = IV_UPD_UPDATING;
+ else
+ net.iv_upd_state = IV_UPD_NORMAL;
+
+ bt_shell_printf("iv_upd_state = IV_UPD_%s\n",
+ iv_update ? "UPDATING" : "NORMAL");
+
+ } else if (iv_update && iv_index != net.iv_index) {
+ bt_shell_printf("IV Update too soon -- Rejecting\n");
+ return;
+ }
+
+ if (iv_index > net.iv_index ||
+ iv_update != net.iv_update) {
+
+ /* Don't reset our seq_num unless
+ * we start using new iv_index */
+ if (!(iv_update && (net.iv_index + 1 == iv_index))) {
+ net.seq_num = 0;
+ net.seq_num_reserved = 100;
+ }
+ }
+
+ if (!net.seq_num || net.iv_index != iv_index ||
+ net.iv_update != iv_update) {
+
+ if (net.seq_num_reserved <= net.seq_num)
+ net.seq_num_reserved = net.seq_num + 100;
+
+ prov_db_local_set_iv_index(iv_index, iv_update,
+ net.provisioner);
+ prov_db_local_set_seq_num(net.seq_num_reserved);
+ }
+
+ net.iv_index = iv_index;
+ net.iv_update = iv_update;
+
+ if (first) {
+ /* Must be done once per Proxy Connection after Beacon RXed */
+ setup_whitelist();
+ if (net.open_cb)
+ net.open_cb(0);
+ }
+}
+
+static bool process_beacon(uint8_t *data, uint8_t size)
+{
+ struct mesh_net_key *net_key;
+ struct net_key_parts *key_part;
+ bool rxed_iv_update, rxed_key_refresh, iv_update;
+ bool my_krf;
+ uint32_t rxed_iv_index, iv_index;
+ uint64_t cmac;
+
+ if (size != 22)
+ return false;
+
+ rxed_key_refresh = (data[1] & 0x01) == 0x01;
+ iv_update = rxed_iv_update = (data[1] & 0x02) == 0x02;
+ iv_index = rxed_iv_index = get_be32(data + 10);
+
+ /* Inhibit recognizing iv_update true-->false
+ * if we have outbound SAR messages in flight */
+ if (net.msg_out != NULL) {
+ if (net.iv_update && !rxed_iv_update)
+ iv_update = true;
+ }
+
+ /* Don't bother going further if nothing has changed */
+ if (iv_index == net.iv_index && iv_update == net.iv_update &&
+ net.iv_upd_state != IV_UPD_INIT)
+ return true;
+
+ /* Find key we are using for SNBs */
+ net_key = find_net_key_by_id(data + 2);
+
+ if (net_key == NULL)
+ return false;
+
+ /* We are Provisioner, and control the key_refresh flag */
+ if (rxed_key_refresh != !!(net_key->phase == 2))
+ return false;
+
+ if (net_key->phase != 2) {
+ my_krf = false;
+ key_part = &net_key->current;
+ } else {
+ my_krf = true;
+ key_part = &net_key->new;
+ }
+
+ /* Ignore for incorrect KR state */
+ if (memcmp(key_part->net_id, data + 2, 8))
+ return false;
+
+ if ((net.iv_index + IV_IDX_DIFF_RANGE < iv_index) ||
+ (iv_index < net.iv_index)) {
+ bt_shell_printf("iv index outside range\n");
+ return false;
+ }
+
+ /* Any behavioral changes must pass CMAC test */
+ if (!mesh_crypto_beacon_cmac(key_part->beacon_key, key_part->net_id,
+ rxed_iv_index, my_krf,
+ rxed_iv_update, &cmac)) {
+ return false;
+ }
+
+ if (cmac != get_be64(data + 14))
+ return false;
+
+ if (iv_update && (net.iv_upd_state > IV_UPD_UPDATING)) {
+ if (iv_index != net.iv_index) {
+ bt_shell_printf("Update too soon -- Rejecting\n");
+ }
+ /* Silently ignore old beacons */
+ return true;
+ }
+
+ beacon_update(net.iv_upd_state == IV_UPD_INIT, iv_update, iv_index);
+
+ return true;
+}
+
+struct decode_params {
+ struct mesh_net_key *net_key;
+ uint8_t *packet;
+ uint32_t iv_index;
+ uint8_t size;
+ bool proxy;
+};
+
+static void try_decode(gpointer data, gpointer user_data)
+{
+ struct mesh_net_key *net_key = data;
+ struct decode_params *decode = user_data;
+ uint8_t nid = decode->packet[0] & 0x7f;
+ uint8_t tmp[29];
+ bool status = false;
+
+ if (decode->net_key)
+ return;
+
+ if (net_key->current.nid == nid)
+ status = mesh_crypto_packet_decode(decode->packet,
+ decode->size, decode->proxy, tmp,
+ decode->iv_index,
+ net_key->current.enc_key,
+ net_key->current.privacy_key);
+
+ if (!status && net_key->new.nid == nid)
+ status = mesh_crypto_packet_decode(decode->packet,
+ decode->size, decode->proxy, tmp,
+ decode->iv_index,
+ net_key->new.enc_key,
+ net_key->new.privacy_key);
+
+ if (status) {
+ decode->net_key = net_key;
+ memcpy(decode->packet, tmp, decode->size);
+ return;
+ }
+}
+
+static struct mesh_net_key *net_packet_decode(bool proxy, uint32_t iv_index,
+ uint8_t *packet, uint8_t size)
+{
+ struct decode_params decode = {
+ .proxy = proxy,
+ .iv_index = iv_index,
+ .packet = packet,
+ .size = size,
+ .net_key = NULL,
+ };
+
+ g_list_foreach(net_keys, try_decode, &decode);
+
+ return decode.net_key;
+}
+
+static void flush_sar(GList **list, struct mesh_sar_msg *sar)
+{
+ *list = g_list_remove(*list, sar);
+
+ if (sar->msg_to)
+ g_source_remove(sar->msg_to);
+
+ if (sar->ack_to)
+ g_source_remove(sar->ack_to);
+
+ g_free(sar);
+}
+
+static void flush_sar_list(GList **list)
+{
+ struct mesh_sar_msg *sar;
+ GList *l = g_list_first(*list);
+
+ while (l) {
+ sar = l->data;
+ flush_sar(list, sar);
+ l = g_list_first(*list);
+ }
+}
+
+static void flush_pkt_list(GList **list)
+{
+ struct mesh_pkt *pkt;
+ GList *l = g_list_first(*list);
+
+ while (l) {
+ pkt = l->data;
+ *list = g_list_remove(*list, pkt);
+ g_free(pkt);
+ }
+}
+
+static void resend_unacked_segs(gpointer data, gpointer user_data)
+{
+ struct mesh_sar_msg *sar = data;
+
+ if (sar->activity_cnt)
+ resend_segs(sar);
+}
+
+static void send_pkt_cmplt(DBusMessage *message, void *user_data)
+{
+ struct mesh_pkt *pkt = user_data;
+ GList *l = g_list_first(net.pkt_out);
+
+ if (l && user_data == l->data) {
+ net.pkt_out = g_list_delete_link(net.pkt_out, l);
+ g_free(pkt);
+ } else {
+ /* This is a serious error, and probable memory leak */
+ bt_shell_printf("ERR: send_pkt_cmplt %p not head of queue\n", pkt);
+ }
+
+ l = g_list_first(net.pkt_out);
+
+ if (l == NULL) {
+ /* If queue is newly empty, resend all SAR outbound packets */
+ g_list_foreach(net.msg_out, resend_unacked_segs, NULL);
+ return;
+ }
+
+ pkt = l->data;
+
+ mesh_gatt_write(net.proxy_in, pkt->data, pkt->len,
+ send_pkt_cmplt, pkt);
+}
+
+static void send_mesh_pkt(struct mesh_pkt *pkt)
+{
+ bool queued = !!(net.pkt_out);
+
+ net.pkt_out = g_list_append(net.pkt_out, pkt);
+
+ if (queued)
+ return;
+
+ mesh_gatt_write(net.proxy_in, pkt->data, pkt->len,
+ send_pkt_cmplt, pkt);
+}
+
+static uint32_t get_next_seq()
+{
+ uint32_t this_seq = net.seq_num++;
+
+ if (net.seq_num + 32 >= net.seq_num_reserved) {
+ net.seq_num_reserved = net.seq_num + 100;
+ prov_db_local_set_seq_num(net.seq_num_reserved);
+ }
+
+ return this_seq;
+}
+
+static void send_seg(struct mesh_sar_msg *sar, uint8_t seg)
+{
+ struct mesh_net_key *net_key;
+ struct net_key_parts *part;
+ struct mesh_pkt *pkt;
+ uint8_t *data;
+
+ net_key = find_net_key_by_idx(sar->net_idx);
+
+ if (net_key == NULL)
+ return;
+
+ /* Choose which components to use to secure pkt */
+ if (net_key->phase == 2 && net_key->new.nid != 0xff)
+ part = &net_key->new;
+ else
+ part = &net_key->current;
+
+ pkt = g_new0(struct mesh_pkt, 1);
+
+ if (pkt == NULL)
+ return;
+
+ /* leave extra byte at start for GATT Proxy type */
+ data = pkt->data + 1;
+
+ SET_PKT_NID(data, part->nid);
+ SET_PKT_IVI(data, sar->iv_index & 1);
+ SET_PKT_CTL(data, sar->ctl);
+ SET_PKT_TTL(data, sar->ttl);
+ SET_PKT_SEQ(data, get_next_seq());
+ SET_PKT_SRC(data, sar->src);
+ SET_PKT_DST(data, sar->dst);
+ SET_PKT_SEGMENTED(data, sar->segmented);
+
+ if (sar->ctl)
+ SET_PKT_OPCODE(data, sar->data[0]);
+ else
+ SET_PKT_AKF_AID(data, sar->akf_aid);
+
+ if (sar->segmented) {
+
+ if (!sar->ctl)
+ SET_PKT_SZMIC(data, sar->szmic);
+
+ SET_PKT_SEQ0(data, sar->seqAuth);
+ SET_PKT_SEGO(data, seg);
+ SET_PKT_SEGN(data, sar->segN);
+
+ memcpy(PKT_TRANS(data) + 4,
+ sar->data + sar->ctl + (seg * 12), 12);
+
+ pkt->len = 9 + 4;
+
+ if (sar->segN == seg)
+ pkt->len += (sar->len - sar->ctl) % 12;
+
+ if (pkt->len == (9 + 4))
+ pkt->len += 12;
+
+ } else {
+ memcpy(PKT_TRANS(data) + 1,
+ sar->data + sar->ctl, 15);
+
+ pkt->len = 9 + 1 + sar->len - sar->ctl;
+ }
+
+ pkt->len += (sar->ctl ? 8 : 4);
+ mesh_crypto_packet_encode(data, pkt->len,
+ part->enc_key,
+ sar->iv_index,
+ part->privacy_key);
+
+
+ /* Prepend GATT_Proxy packet type */
+ if (sar->proxy)
+ pkt->data[0] = PROXY_CONFIG_PDU;
+ else
+ pkt->data[0] = PROXY_NETWORK_PDU;
+
+ pkt->len++;
+
+ send_mesh_pkt(pkt);
+}
+
+static void resend_segs(struct mesh_sar_msg *sar)
+{
+ uint32_t ack = 1;
+ uint8_t i;
+
+ sar->activity_cnt = 0;
+
+ for (i = 0; i <= sar->segN; i++, ack <<= 1) {
+ if (!(ack & sar->ack))
+ send_seg(sar, i);
+ }
+}
+
+static bool ack_rxed(bool to, uint16_t src, uint16_t dst, bool obo,
+ uint16_t seq0, uint32_t ack_flags)
+{
+ struct mesh_sar_msg *sar = find_sar_out_by_dst(src);
+ uint32_t full_ack;
+
+ /* Silently ignore unknown (stale?) ACKs */
+ if (sar == NULL)
+ return true;
+
+ full_ack = 0xffffffff >> (31 - sar->segN);
+
+ sar->ack |= (ack_flags & full_ack);
+
+ if (sar->ack == full_ack) {
+ /* Outbound message 100% received by remote node */
+ flush_sar(&net.msg_out, sar);
+ return true;
+ }
+
+ /* Because we are GATT, and slow, only resend PKTs if it is
+ * time *and* our outbound PKT queue is empty. */
+ sar->activity_cnt++;
+
+ if (net.pkt_out == NULL)
+ resend_segs(sar);
+
+ return true;
+}
+
+static bool proxy_ctl_rxed(uint16_t net_idx, uint32_t iv_index,
+ uint8_t ttl, uint32_t seq_num, uint16_t src, uint16_t dst,
+ uint8_t *trans, uint16_t len)
+{
+ if (len < 1)
+ return false;
+
+ switch(trans[0]) {
+ case FILTER_STATUS:
+ if (len != 4)
+ return false;
+
+ net.blacklist = !!(trans[1] == BLACKLIST_FILTER);
+ bt_shell_printf("Proxy %slist filter length: %d\n",
+ net.blacklist ? "Black" : "White",
+ get_be16(trans + 2));
+
+ return true;
+
+ default:
+ return false;
+ }
+
+ return false;
+}
+
+static bool ctl_rxed(uint16_t net_idx, uint32_t iv_index,
+ uint8_t ttl, uint32_t seq_num, uint16_t src, uint16_t dst,
+ uint8_t *trans, uint16_t len)
+{
+ /* TODO: Handle control messages */
+
+ /* Per Mesh Profile 3.6.5.10 */
+ if (trans[0] == NET_OP_HEARTBEAT) {
+ uint16_t feat = get_be16(trans + 2);
+
+ bt_shell_printf("HEARTBEAT src: %4.4x dst: %4.4x \
+ TTL: %2.2x feat: %s%s%s%s\n",
+ src, dst, trans[1],
+ (feat & MESH_FEATURE_RELAY) ? "relay " : "",
+ (feat & MESH_FEATURE_PROXY) ? "proxy " : "",
+ (feat & MESH_FEATURE_FRIEND) ? "friend " : "",
+ (feat & MESH_FEATURE_LPN) ? "lpn" : "");
+ return true;
+ }
+
+ bt_shell_printf("unrecognized control message src:%4.4x dst:%4.4x len:%d\n",
+ src, dst, len);
+ print_byte_array("msg: ", trans, len);
+ return false;
+}
+
+struct decrypt_params {
+ uint8_t *nonce;
+ uint8_t *aad;
+ uint8_t *out_msg;
+ uint8_t *trans;
+ uint32_t iv_index;
+ uint32_t seq_num;
+ uint16_t src;
+ uint16_t dst;
+ uint16_t len;
+ uint16_t net_idx;
+ uint16_t app_idx;
+ uint8_t akf_aid;
+ bool szmic;
+};
+
+
+static void try_decrypt(gpointer data, gpointer user_data)
+{
+ struct mesh_app_key *app_key = data;
+ struct decrypt_params *decrypt = user_data;
+ size_t mic_size = decrypt->szmic ? sizeof(uint64_t) : sizeof(uint32_t);
+ bool status = false;
+
+ /* Already done... Nothing to do */
+ if (decrypt->app_idx != APP_IDX_INVALID)
+ return;
+
+ /* Don't decrypt on Appkeys not owned by this NetKey */
+ if (app_key->net_idx != decrypt->net_idx)
+ return;
+
+ /* Test and decrypt against current key copy */
+ if (app_key->current.akf_aid == decrypt->akf_aid)
+ status = mesh_crypto_aes_ccm_decrypt(decrypt->nonce,
+ app_key->current.key,
+ decrypt->aad, decrypt->aad ? 16 : 0,
+ decrypt->trans, decrypt->len,
+ decrypt->out_msg, NULL, mic_size);
+
+ /* Test and decrypt against new key copy */
+ if (!status && app_key->new.akf_aid == decrypt->akf_aid)
+ status = mesh_crypto_aes_ccm_decrypt(decrypt->nonce,
+ app_key->new.key,
+ decrypt->aad, decrypt->aad ? 16 : 0,
+ decrypt->trans, decrypt->len,
+ decrypt->out_msg, NULL, mic_size);
+
+ /* If successful, terminate with successful App IDX */
+ if (status)
+ decrypt->app_idx = app_key->generic.idx;
+}
+
+static uint16_t access_pkt_decrypt(uint8_t *nonce, uint8_t *aad,
+ uint16_t net_idx, uint8_t akf_aid, bool szmic,
+ uint8_t *trans, uint16_t len)
+{
+ uint8_t *out_msg;
+ struct decrypt_params decrypt = {
+ .nonce = nonce,
+ .aad = aad,
+ .net_idx = net_idx,
+ .akf_aid = akf_aid,
+ .szmic = szmic,
+ .trans = trans,
+ .len = len,
+ .app_idx = APP_IDX_INVALID,
+ };
+
+ out_msg = g_malloc(len);
+
+ if (out_msg == NULL)
+ return false;
+
+ decrypt.out_msg = out_msg;
+
+ g_list_foreach(app_keys, try_decrypt, &decrypt);
+
+ if (decrypt.app_idx != APP_IDX_INVALID)
+ memcpy(trans, out_msg, len);
+
+ g_free(out_msg);
+
+ return decrypt.app_idx;
+}
+
+static bool access_rxed(uint8_t *nonce, uint16_t net_idx,
+ uint32_t iv_index, uint32_t seq_num,
+ uint16_t src, uint16_t dst,
+ uint8_t akf_aid, bool szmic, uint8_t *trans, uint16_t len)
+{
+ uint16_t app_idx = access_pkt_decrypt(nonce, NULL,
+ net_idx, akf_aid, szmic, trans, len);
+
+ if (app_idx != APP_IDX_INVALID) {
+ len -= szmic ? sizeof(uint64_t) : sizeof(uint32_t);
+
+ node_local_data_handler(src, dst, iv_index, seq_num,
+ app_idx, trans, len);
+ return true;
+ }
+
+ return false;
+}
+
+static void try_virt_decrypt(gpointer data, gpointer user_data)
+{
+ struct mesh_virt_addr *virt = data;
+ struct decrypt_params *decrypt = user_data;
+
+ if (decrypt->app_idx != APP_IDX_INVALID || decrypt->dst != virt->va16)
+ return;
+
+ decrypt->app_idx = access_pkt_decrypt(decrypt->nonce,
+ virt->va128,
+ decrypt->net_idx, decrypt->akf_aid,
+ decrypt->szmic, decrypt->trans, decrypt->len);
+
+ if (decrypt->app_idx != APP_IDX_INVALID) {
+ uint16_t len = decrypt->len;
+
+ len -= decrypt->szmic ? sizeof(uint64_t) : sizeof(uint32_t);
+
+ node_local_data_handler(decrypt->src, virt->va32,
+ decrypt->iv_index, decrypt->seq_num,
+ decrypt->app_idx, decrypt->trans, len);
+ }
+}
+
+static bool virtual_rxed(uint8_t *nonce, uint16_t net_idx,
+ uint32_t iv_index, uint32_t seq_num,
+ uint16_t src, uint16_t dst,
+ uint8_t akf_aid, bool szmic, uint8_t *trans, uint16_t len)
+{
+ struct decrypt_params decrypt = {
+ .nonce = nonce,
+ .net_idx = net_idx,
+ .iv_index = iv_index,
+ .seq_num = seq_num,
+ .src = dst,
+ .dst = dst,
+ .akf_aid = akf_aid,
+ .szmic = szmic,
+ .trans = trans,
+ .len = len,
+ .app_idx = APP_IDX_INVALID,
+ };
+
+ /* Cycle through known virtual addresses */
+ g_list_foreach(virt_addrs, try_virt_decrypt, &decrypt);
+
+ if (decrypt.app_idx != APP_IDX_INVALID)
+ return true;
+
+ return false;
+}
+
+static bool msg_rxed(uint16_t net_idx, uint32_t iv_index, bool szmic,
+ uint8_t ttl, uint32_t seq_num, uint32_t seq_auth,
+ uint16_t src, uint16_t dst,
+ uint8_t *trans, uint16_t len)
+{
+ uint8_t akf_aid = TRANS_AKF_AID(trans);
+ bool result;
+ size_t mic_size = szmic ? sizeof(uint64_t) : sizeof(uint32_t);
+ uint8_t nonce[13];
+ uint8_t *dev_key;
+ uint8_t *out = NULL;
+
+ if (!TRANS_AKF(trans)) {
+ /* Compose Nonce */
+ result = mesh_crypto_device_nonce(seq_auth, src, dst,
+ iv_index, szmic, nonce);
+
+ if (!result) return false;
+
+ out = g_malloc0(TRANS_LEN(trans, len));
+ if (out == NULL) return false;
+
+ /* If we are provisioner, we probably RXed on remote Dev Key */
+ if (net.provisioner) {
+ dev_key = node_get_device_key(node_find_by_addr(src));
+
+ if (dev_key == NULL)
+ goto local_dev_key;
+ } else
+ goto local_dev_key;
+
+ result = mesh_crypto_aes_ccm_decrypt(nonce, dev_key,
+ NULL, 0,
+ TRANS_PAYLOAD(trans), TRANS_LEN(trans, len),
+ out, NULL, mic_size);
+
+ if (result) {
+ node_local_data_handler(src, dst,
+ iv_index, seq_num, APP_IDX_DEV,
+ out, TRANS_LEN(trans, len) - mic_size);
+ goto done;
+ }
+
+local_dev_key:
+ /* Always fallback to the local Dev Key */
+ dev_key = node_get_device_key(node_get_local_node());
+
+ if (dev_key == NULL)
+ goto done;
+
+ result = mesh_crypto_aes_ccm_decrypt(nonce, dev_key,
+ NULL, 0,
+ TRANS_PAYLOAD(trans), TRANS_LEN(trans, len),
+ out, NULL, mic_size);
+
+ if (result) {
+ node_local_data_handler(src, dst,
+ iv_index, seq_num, APP_IDX_DEV,
+ out, TRANS_LEN(trans, len) - mic_size);
+ goto done;
+ }
+
+ goto done;
+ }
+
+ result = mesh_crypto_application_nonce(seq_auth, src, dst,
+ iv_index, szmic, nonce);
+
+ if (!result) goto done;
+
+ /* If Virtual destination wrap the Access decoder with Virtual */
+ if (IS_VIRTUAL(dst)) {
+ result = virtual_rxed(nonce, net_idx, iv_index, seq_num,
+ src, dst, akf_aid, szmic,
+ TRANS_PAYLOAD(trans), TRANS_LEN(trans, len));
+ goto done;
+ }
+
+ /* Try all matching App Keys until success or exhaustion */
+ result = access_rxed(nonce, net_idx, iv_index, seq_num,
+ src, dst, akf_aid, szmic,
+ TRANS_PAYLOAD(trans), TRANS_LEN(trans, len));
+
+done:
+ if (out != NULL)
+ g_free(out);
+
+ return result;
+}
+
+static void send_sar_ack(struct mesh_sar_msg *sar)
+{
+ uint8_t ack[7];
+
+ sar->activity_cnt = 0;
+
+ memset(ack, 0, sizeof(ack));
+ SET_TRANS_OPCODE(ack, NET_OP_SEG_ACKNOWLEDGE);
+ SET_TRANS_SEQ0(ack, sar->seqAuth);
+ SET_TRANS_ACK(ack, sar->ack);
+
+ net_ctl_msg_send(0xff, sar->dst, sar->src, ack, sizeof(ack));
+}
+
+static gboolean sar_out_ack_timeout(void *user_data)
+{
+ struct mesh_sar_msg *sar = user_data;
+
+ sar->activity_cnt++;
+
+ /* Because we are GATT, and slow, only resend PKTs if it is
+ * time *and* our outbound PKT queue is empty. */
+ if (net.pkt_out == NULL)
+ resend_segs(sar);
+
+ /* Only add resent SAR pkts to empty queue */
+ return true;
+}
+
+static gboolean sar_out_msg_timeout(void *user_data)
+{
+ struct mesh_sar_msg *sar = user_data;
+
+ /* msg_to will expire when we return false */
+ sar->msg_to = 0;
+
+ flush_sar(&net.msg_out, sar);
+
+ return false;
+}
+
+static gboolean sar_in_ack_timeout(void *user_data)
+{
+ struct mesh_sar_msg *sar = user_data;
+ uint32_t full_ack = 0xffffffff >> (31 - sar->segN);
+
+ if (sar->activity_cnt || sar->ack != full_ack)
+ send_sar_ack(sar);
+
+ return true;
+}
+
+static gboolean sar_in_msg_timeout(void *user_data)
+{
+ struct mesh_sar_msg *sar = user_data;
+
+ /* msg_to will expire when we return false */
+ sar->msg_to = 0;
+
+ flush_sar(&net.sar_in, sar);
+
+ return false;
+}
+
+static uint32_t calc_seqAuth(uint32_t seq_num, uint8_t *trans)
+{
+ uint32_t seqAuth = seq_num & ~0x1fff;
+
+ seqAuth |= TRANS_SEQ0(trans);
+
+ return seqAuth;
+}
+
+static bool seg_rxed(uint16_t net_idx, uint32_t iv_index, bool ctl,
+ uint8_t ttl, uint32_t seq_num, uint16_t src, uint16_t dst,
+ uint8_t *trans, uint16_t len)
+{
+ struct mesh_sar_msg *sar;
+ uint32_t seqAuth = calc_seqAuth(seq_num, trans);
+ uint8_t segN, segO;
+ uint32_t old_ack, full_ack, last_ack_mask;
+ bool send_ack, result = false;
+
+ segN = TRANS_SEGN(trans);
+ segO = TRANS_SEGO(trans);
+
+ /* Only support single incoming SAR'd message per SRC */
+ sar = find_sar_in_by_src(src);
+
+ /* Reuse existing SAR structure if appropriate */
+ if (sar) {
+ uint64_t iv_seqAuth = (uint64_t)iv_index << 32 | seqAuth;
+ uint64_t old_iv_seqAuth = (uint64_t)sar->iv_index << 32 |
+ sar->seqAuth;
+ if (old_iv_seqAuth < iv_seqAuth) {
+
+ flush_sar(&net.sar_in, sar);
+ sar = NULL;
+
+ } else if (old_iv_seqAuth > iv_seqAuth) {
+
+ /* New segment is Stale. Silently ignore */
+ return false;
+
+ } else if (segN != sar->segN) {
+
+ /* Remote side sent conflicting data: abandon */
+ flush_sar(&net.sar_in, sar);
+ sar = NULL;
+
+ }
+ }
+
+ if (sar == NULL) {
+ sar = g_malloc0(sizeof(*sar) + (12 * segN));
+
+ if (sar == NULL)
+ return false;
+
+ sar->net_idx = net_idx;
+ sar->iv_index = iv_index;
+ sar->ctl = ctl;
+ sar->ttl = ttl;
+ sar->seqAuth = seqAuth;
+ sar->src = src;
+ sar->dst = dst;
+ sar->segmented = true;
+ sar->szmic = TRANS_SZMIC(trans);
+ sar->segN = segN;
+
+ /* In all cases, the reassembled packet should begin with the
+ * same first octet of all segments, minus the SEGMENTED flag */
+ sar->data[0] = trans[0] & 0x7f;
+
+ net.sar_in = g_list_append(net.sar_in, sar);
+
+ /* Setup expiration timers */
+ if (IS_UNICAST(dst))
+ sar->ack_to = g_timeout_add(5000,
+ sar_in_ack_timeout, sar);
+
+ sar->msg_to = g_timeout_add(60000, sar_in_msg_timeout, sar);
+ }
+
+ /* If last segment, calculate full msg size */
+ if (segN == segO)
+ sar->len = (segN * 12) + len - 3;
+
+ /* Copy to correct offset */
+ memcpy(sar->data + 1 + (12 * segO), trans + 4, 12);
+
+ full_ack = 0xffffffff >> (31 - segN);
+ last_ack_mask = 0xffffffff << segO;
+ old_ack = sar->ack;
+ sar->ack |= 1 << segO;
+ send_ack = false;
+
+ /* Determine if we should forward message */
+ if (sar->ack == full_ack && old_ack != full_ack) {
+
+ /* First time we have seen this complete message */
+ send_ack = true;
+
+ if (ctl)
+ result = ctl_rxed(sar->net_idx, sar->iv_index,
+ sar->ttl, sar->seqAuth, sar->src,
+ sar->dst, sar->data, sar->len);
+ else
+ result = msg_rxed(sar->net_idx, sar->iv_index,
+ sar->szmic, sar->ttl,
+ seq_num, sar->seqAuth, sar->src,
+ sar->dst, sar->data, sar->len);
+ }
+
+ /* Never Ack Group addressed SAR messages */
+ if (!IS_UNICAST(dst))
+ return result;
+
+ /* Tickle the ACK system so it knows we are still RXing segments */
+ sar->activity_cnt++;
+
+ /* Determine if we should ACK */
+ if (old_ack == sar->ack)
+ /* Let the timer generate repeat ACKs as needed */
+ send_ack = false;
+ else if ((last_ack_mask & sar->ack) == (last_ack_mask & full_ack))
+ /* If this was largest segO outstanding segment, we ACK */
+ send_ack = true;
+
+ if (send_ack)
+ send_sar_ack(sar);
+
+ return result;
+}
+
+bool net_data_ready(uint8_t *msg, uint8_t len)
+{
+ uint8_t type = *msg++;
+ uint32_t iv_index = net.iv_index;
+ struct mesh_net_key *net_key;
+
+ if (len-- < 10) return false;
+
+ if (type == PROXY_MESH_BEACON)
+ return process_beacon(msg, len);
+ else if (type > PROXY_CONFIG_PDU)
+ return false;
+
+ /* RXed iv_index must be equal or 1 less than local iv_index */
+ /* With the clue being high-order bit of first octet */
+ if (!!(iv_index & 0x01) != !!(msg[0] & 0x80)) {
+ if (iv_index)
+ iv_index--;
+ else
+ return false;
+ }
+
+ net_key = net_packet_decode(type == PROXY_CONFIG_PDU,
+ iv_index, msg, len);
+
+ if (net_key == NULL)
+ return false;
+
+ /* CTL packets have 64 bit network MIC, otherwise 32 bit MIC */
+ len -= PKT_CTL(msg) ? sizeof(uint64_t) : sizeof(uint32_t);
+
+ if (type == PROXY_CONFIG_PDU) {
+
+ /* Proxy Configuration DST messages must be 0x0000 */
+ if (PKT_DST(msg))
+ return false;
+
+ return proxy_ctl_rxed(net_key->generic.idx,
+ iv_index, PKT_TTL(msg), PKT_SEQ(msg),
+ PKT_SRC(msg), PKT_DST(msg),
+ PKT_TRANS(msg), PKT_TRANS_LEN(len));
+
+ } if (PKT_CTL(msg) && PKT_OPCODE(msg) == NET_OP_SEG_ACKNOWLEDGE) {
+
+ return ack_rxed(false, PKT_SRC(msg), PKT_DST(msg),
+ PKT_OBO(msg), PKT_SEQ0(msg), PKT_ACK(msg));
+
+ } else if (PKT_SEGMENTED(msg)) {
+
+ return seg_rxed(net_key->generic.idx, iv_index, PKT_CTL(msg),
+ PKT_TTL(msg), PKT_SEQ(msg),
+ PKT_SRC(msg), PKT_DST(msg),
+ PKT_TRANS(msg), PKT_TRANS_LEN(len));
+
+ } else if (!PKT_CTL(msg)){
+
+ return msg_rxed(net_key->generic.idx,
+ iv_index, false, PKT_TTL(msg), PKT_SEQ(msg),
+ PKT_SEQ(msg), PKT_SRC(msg), PKT_DST(msg),
+ PKT_TRANS(msg), PKT_TRANS_LEN(len));
+ } else {
+
+ return ctl_rxed(net_key->generic.idx,
+ iv_index, PKT_TTL(msg), PKT_SEQ(msg),
+ PKT_SRC(msg), PKT_DST(msg),
+ PKT_TRANS(msg), PKT_TRANS_LEN(len));
+
+ }
+
+ return false;
+}
+
+bool net_session_open(GDBusProxy *data_in, bool provisioner,
+ net_mesh_session_open_callback cb)
+{
+ if (net.proxy_in)
+ return false;
+
+ net.proxy_in = data_in;
+ net.iv_upd_state = IV_UPD_INIT;
+ net.blacklist = false;
+ net.provisioner = provisioner;
+ net.open_cb = cb;
+ flush_pkt_list(&net.pkt_out);
+ return true;
+}
+
+void net_session_close(GDBusProxy *data_in)
+{
+ if (net.proxy_in == data_in)
+ net.proxy_in = NULL;
+
+ flush_sar_list(&net.sar_in);
+ flush_sar_list(&net.msg_out);
+ flush_pkt_list(&net.pkt_out);
+}
+
+bool net_register_unicast(uint16_t unicast, uint8_t count)
+{
+ /* TODO */
+ return true;
+}
+
+bool net_register_group(uint16_t group_addr)
+{
+ /* TODO */
+ return true;
+}
+
+uint32_t net_register_virtual(uint8_t buf[16])
+{
+ /* TODO */
+ return 0;
+}
+
+static bool get_enc_keys(uint16_t app_idx, uint16_t dst,
+ uint8_t *akf_aid, uint8_t **app_enc_key,
+ uint16_t *net_idx)
+{
+ if (app_idx == APP_IDX_DEV) {
+ struct mesh_node *node;
+ uint8_t *enc_key = NULL;
+
+ if (net.provisioner) {
+ /* Default to Remote Device Key when Provisioner */
+ node = node_find_by_addr(dst);
+ enc_key = node_get_device_key(node);
+ }
+
+ if (enc_key == NULL) {
+ /* Use Local node Device Key */
+ node = node_get_local_node();
+ enc_key = node_get_device_key(node);
+ }
+
+ if (enc_key == NULL || node == NULL)
+ return false;
+
+ if (akf_aid) *akf_aid = 0;
+ if (app_enc_key) *app_enc_key = enc_key;
+ if (net_idx) *net_idx = node_get_primary_net_idx(node);
+
+ } else {
+ struct mesh_app_key *app_key = find_app_key_by_idx(app_idx);
+ struct mesh_net_key *net_key;
+ bool phase_two;
+
+
+ if (app_key == NULL)
+ return false;
+
+ net_key = find_net_key_by_idx(app_key->net_idx);
+
+ if (net_key == NULL)
+ return false;
+
+ if (net_idx) *net_idx = app_key->net_idx;
+
+ phase_two = !!(net_key->phase == 2);
+
+ if (phase_two && app_key->new.akf_aid != 0xff) {
+ if (app_enc_key) *app_enc_key = app_key->new.key;
+ if (akf_aid) *akf_aid = app_key->new.akf_aid;
+ } else {
+ if (app_enc_key) *app_enc_key = app_key->current.key;
+ if (akf_aid) *akf_aid = app_key->current.akf_aid;
+ }
+ }
+
+ return true;
+}
+
+bool net_ctl_msg_send(uint8_t ttl, uint16_t src, uint16_t dst,
+ uint8_t *buf, uint16_t len)
+{
+ struct mesh_node *node = node_get_local_node();
+ struct mesh_sar_msg sar_ctl;
+
+ /* For simplicity, we will reject segmented OB CTL messages */
+ if (len > 12 || node == NULL || buf == NULL || buf[0] & 0x80)
+ return false;
+
+ if (!src) {
+ src = node_get_primary(node);
+
+ if (!src)
+ return false;
+ }
+
+ if (ttl == 0xff)
+ ttl = net.default_ttl;
+
+ memset(&sar_ctl, 0, sizeof(sar_ctl));
+
+ if (!dst)
+ sar_ctl.proxy = true;
+
+ /* Get the default net_idx for remote device (or local) */
+ get_enc_keys(APP_IDX_DEV, dst, NULL, NULL, &sar_ctl.net_idx);
+ sar_ctl.ctl = true;
+ sar_ctl.iv_index = net.iv_index - net.iv_update;
+ sar_ctl.ttl = ttl;
+ sar_ctl.src = src;
+ sar_ctl.dst = dst;
+ sar_ctl.len = len;
+ memcpy(sar_ctl.data, buf, len);
+ send_seg(&sar_ctl, 0);
+
+ return true;
+}
+
+bool net_access_layer_send(uint8_t ttl, uint16_t src, uint32_t dst,
+ uint16_t app_idx, uint8_t *buf, uint16_t len)
+{
+ struct mesh_node *node = node_get_local_node();
+ struct mesh_sar_msg *sar;
+ uint8_t *app_enc_key = NULL;
+ uint8_t *aad = NULL;
+ uint32_t mic32;
+ uint8_t aad_len = 0;
+ uint8_t i, j, ackless_retries = 0;
+ uint8_t segN, akf_aid;
+ uint16_t net_idx;
+ bool result;
+
+ if (len > 384 || node == NULL)
+ return false;
+
+ if (!src)
+ src = node_get_primary(node);
+
+ if (!src || !dst)
+ return false;
+
+ if (ttl == 0xff)
+ ttl = net.default_ttl;
+
+ if (IS_VIRTUAL(dst)) {
+ struct mesh_virt_addr *virt = find_virt_by_dst(dst);
+
+ if (virt == NULL)
+ return false;
+
+ dst = virt->va16;
+ aad = virt->va128;
+ aad_len = sizeof(virt->va128);
+ }
+
+ result = get_enc_keys(app_idx, dst,
+ &akf_aid, &app_enc_key, &net_idx);
+
+ if (!result)
+ return false;
+
+ segN = SEG_MAX(len + sizeof(mic32));
+
+ /* Only one ACK required SAR message per destination at a time */
+ if (segN && IS_UNICAST(dst)) {
+ sar = find_sar_out_by_dst(dst);
+
+ if (sar)
+ flush_sar(&net.msg_out, sar);
+ }
+
+ sar = g_malloc0(sizeof(struct mesh_sar_msg) + (segN * 12));
+
+ if (sar == NULL)
+ return false;
+
+ if (segN)
+ sar->segmented = true;
+
+ sar->ttl = ttl;
+ sar->segN = segN;
+ sar->seqAuth = net.seq_num;
+ sar->iv_index = net.iv_index - net.iv_update;
+ sar->net_idx = net_idx;
+ sar->src = src;
+ sar->dst = dst;
+ sar->akf_aid = akf_aid;
+ sar->len = len + sizeof(uint32_t);
+
+ mesh_crypto_application_encrypt(akf_aid,
+ sar->seqAuth, src,
+ dst, sar->iv_index,
+ app_enc_key,
+ aad, aad_len,
+ buf, len,
+ sar->data, &mic32,
+ sizeof(uint32_t));
+
+ /* If sending as a segmented message to a non-Unicast (thus non-ACKing)
+ * destination, send each segments multiple times. */
+ if (!IS_UNICAST(dst) && segN)
+ ackless_retries = 4;
+
+ for (j = 0; j <= ackless_retries; j++) {
+ for (i = 0; i <= segN; i++)
+ send_seg(sar, i);
+ }
+
+ if (IS_UNICAST(dst) && segN) {
+ net.msg_out = g_list_append(net.msg_out, sar);
+ sar->ack_to = g_timeout_add(2000, sar_out_ack_timeout, sar);
+ sar->msg_to = g_timeout_add(60000, sar_out_msg_timeout, sar);
+ } else
+ g_free(sar);
+
+ return true;
+}
+
+bool net_set_default_ttl(uint8_t ttl)
+{
+ if (ttl > 0x7f)
+ return false;
+
+ net.default_ttl = ttl;
+ return true;
+}
+
+uint8_t net_get_default_ttl()
+{
+ return net.default_ttl;
+}
+
+bool net_set_seq_num(uint32_t seq_num)
+{
+ if (seq_num > 0xffffff)
+ return false;
+
+ net.seq_num = seq_num;
+ return true;
+}
+
+uint32_t net_get_seq_num()
+{
+ return net.seq_num;
+}
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2017 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
+ *
+ */
+
+#include "gdbus/gdbus.h"
+
+typedef void (*net_mesh_session_open_callback)(int status);
+
+uint32_t net_get_iv_index(bool *iv_update);
+bool net_get_key(uint16_t net_idx, uint8_t *key);
+bool net_get_flags(uint16_t net_idx, uint8_t *out_flags);
+void net_set_iv_index(uint32_t index, bool update);
+uint32_t get_sequence_number(void);
+void set_sequence_number(uint32_t seq_number);
+uint16_t net_validate_proxy_beacon(const uint8_t *proxy_beacon);
+bool net_add_address_pool(uint16_t min, uint16_t max);
+uint16_t net_obtain_address(uint8_t num_elements);
+bool net_reserve_address_range(uint16_t base, uint8_t num_elements);
+void net_release_address(uint16_t addr, uint8_t num_elements);
+bool net_session_open(GDBusProxy *data_in, bool provisioner,
+ net_mesh_session_open_callback cb);
+void net_session_close(GDBusProxy *data_in);
+
+bool net_data_ready(uint8_t *ptr, uint8_t len);
+bool net_access_layer_send(uint8_t ttl, uint16_t src, uint32_t dst,
+ uint16_t app_idx, uint8_t *buf, uint16_t len);
+bool net_ctl_msg_send(uint8_t ttl, uint16_t src, uint16_t dst,
+ uint8_t *buf, uint16_t len);
+bool net_set_default_ttl(uint8_t ttl);
+uint8_t net_get_default_ttl(void);
+bool net_set_seq_num(uint32_t seq_num);
+uint32_t net_get_seq_num(void);
+void net_dest_ref(uint16_t dst);
+void net_dest_unref(uint16_t dst);
+bool net_register_unicast(uint16_t unicast, uint8_t count);
+bool net_register_group(uint16_t group_addr);
+uint32_t net_register_virtual(uint8_t buf[16]);
+bool mesh_model_recv(uint16_t app_idx, uint16_t src, uint32_t dst,
+ uint8_t *payload, uint16_t len);
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2017 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>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <sys/uio.h>
+#include <wordexp.h>
+#include <inttypes.h>
+
+#include <glib.h>
+
+#include "src/shared/util.h"
+#include "src/shared/shell.h"
+#include "gdbus/gdbus.h"
+#include "mesh/mesh-net.h"
+#include "mesh/config-model.h"
+#include "mesh/node.h"
+#include "mesh/keys.h"
+#include "mesh/gatt.h"
+#include "mesh/net.h"
+#include "mesh/prov-db.h"
+#include "mesh/util.h"
+
+struct mesh_model {
+ struct mesh_model_ops cbs;
+ void *user_data;
+ GList *bindings;
+ GList *subscriptions;
+ uint32_t id;
+ struct mesh_publication *pub;
+};
+
+struct mesh_element {
+ GList *models;
+ uint16_t loc;
+ uint8_t index;
+};
+
+struct mesh_node {
+ const char *name;
+ GList *net_keys;
+ GList *app_keys;
+ void *prov;
+ GList *elements;
+ uint32_t iv_index;
+ uint32_t seq_number;
+ uint16_t primary_net_idx;
+ uint16_t primary;
+ uint16_t oob;
+ uint16_t features;
+ uint8_t dev_uuid[16];
+ uint8_t dev_key[16];
+ uint8_t num_ele;
+ uint8_t ttl;
+ bool provisioner;
+ struct mesh_node_composition *comp;
+};
+
+static GList *nodes;
+
+static struct mesh_node *local_node;
+
+static int match_node_unicast(const void *a, const void *b)
+{
+ const struct mesh_node *node = a;
+ uint16_t dst = GPOINTER_TO_UINT(b);
+
+ if (dst >= node->primary &&
+ dst <= (node->primary + node->num_ele - 1))
+ return 0;
+
+ return -1;
+}
+
+static int match_device_uuid(const void *a, const void *b)
+{
+ const struct mesh_node *node = a;
+ const uint8_t *uuid = b;
+
+ return memcmp(node->dev_uuid, uuid, 16);
+}
+
+static int match_element_idx(const void *a, const void *b)
+{
+ const struct mesh_element *element = a;
+ uint32_t index = GPOINTER_TO_UINT(b);
+
+ return (element->index == index) ? 0 : -1;
+}
+
+static int match_model_id(const void *a, const void *b)
+{
+ const struct mesh_model *model = a;
+ uint32_t id = GPOINTER_TO_UINT(b);
+
+ return (model->id == id) ? 0 : -1;
+}
+
+struct mesh_node *node_find_by_addr(uint16_t addr)
+{
+ GList *l;
+
+ if (!IS_UNICAST(addr))
+ return NULL;
+
+ l = g_list_find_custom(nodes, GUINT_TO_POINTER(addr),
+ match_node_unicast);
+
+ if (l)
+ return l->data;
+ else
+ return NULL;
+}
+
+struct mesh_node *node_find_by_uuid(uint8_t uuid[16])
+{
+ GList *l;
+
+ l = g_list_find_custom(nodes, uuid, match_device_uuid);
+
+ if (l)
+ return l->data;
+ else
+ return NULL;
+}
+
+struct mesh_node *node_create_new(struct prov_svc_data *prov)
+{
+ struct mesh_node *node;
+
+ if (node_find_by_uuid(prov->dev_uuid))
+ return NULL;
+
+ node = g_malloc0(sizeof(struct mesh_node));
+ if (!node)
+ return NULL;
+
+ memcpy(node->dev_uuid, prov->dev_uuid, 16);
+ node->oob = prov->oob;
+ nodes = g_list_append(nodes, node);
+
+ return node;
+}
+
+struct mesh_node *node_new(void)
+{
+ struct mesh_node *node;
+
+ node = g_malloc0(sizeof(struct mesh_node));
+ if (!node)
+ return NULL;
+
+ nodes = g_list_append(nodes, node);
+
+ return node;
+}
+
+static void model_free(void *data)
+{
+ struct mesh_model *model = data;
+
+ g_list_free(model->bindings);
+ g_list_free(model->subscriptions);
+ g_free(model->pub);
+ g_free(model);
+}
+
+static void element_free(void *data)
+{
+ struct mesh_element *element = data;
+
+ g_list_free_full(element->models, model_free);
+ g_free(element);
+}
+
+static void free_node_resources(void *data)
+{
+ struct mesh_node *node = data;
+ g_list_free(node->net_keys);
+ g_list_free(node->app_keys);
+
+ g_list_free_full(node->elements, element_free);
+
+ if(node->comp)
+ g_free(node->comp);
+
+ g_free(node);
+}
+
+void node_free(struct mesh_node *node)
+{
+ if (!node)
+ return;
+ nodes = g_list_remove(nodes, node);
+ free_node_resources(node);
+}
+
+void node_cleanup(void)
+{
+ g_list_free_full(nodes, free_node_resources);
+ local_node = NULL;
+}
+
+bool node_is_provisioned(struct mesh_node *node)
+{
+ return (!IS_UNASSIGNED(node->primary));
+}
+
+void *node_get_prov(struct mesh_node *node)
+{
+ return node->prov;
+}
+
+void node_set_prov(struct mesh_node *node, void *prov)
+{
+ node->prov = prov;
+}
+
+bool node_app_key_add(struct mesh_node *node, uint16_t idx)
+{
+ uint32_t index;
+ uint16_t net_idx;
+
+ if (!node)
+ return false;
+
+ net_idx = keys_app_key_get_bound(idx);
+ if (net_idx == NET_IDX_INVALID)
+ return false;
+
+ if (!g_list_find(node->net_keys, GUINT_TO_POINTER(net_idx)))
+ return false;
+
+ index = (net_idx << 16) + idx;
+
+ if (g_list_find(node->app_keys, GUINT_TO_POINTER(index)))
+ return false;
+
+ node->app_keys = g_list_append(node->app_keys, GUINT_TO_POINTER(index));
+
+ return true;
+}
+
+bool node_net_key_add(struct mesh_node *node, uint16_t index)
+{
+ if(!node)
+ return false;
+
+ if (g_list_find(node->net_keys, GUINT_TO_POINTER(index)))
+ return false;
+
+ node->net_keys = g_list_append(node->net_keys, GUINT_TO_POINTER(index));
+ return true;
+}
+
+bool node_net_key_delete(struct mesh_node *node, uint16_t index)
+{
+ GList *l;
+
+ if(!node)
+ return false;
+
+ l = g_list_find(node->net_keys, GUINT_TO_POINTER(index));
+ if (!l)
+ return false;
+
+ node->net_keys = g_list_remove(node->net_keys,
+ GUINT_TO_POINTER(index));
+ /* TODO: remove all associated app keys and bindings */
+ return true;
+}
+
+bool node_app_key_delete(struct mesh_node *node, uint16_t net_idx,
+ uint16_t idx)
+{
+ GList *l;
+ uint32_t index;
+
+ if(!node)
+ return false;
+
+ index = (net_idx << 16) + idx;
+
+ l = g_list_find(node->app_keys, GUINT_TO_POINTER(index));
+ if (!l)
+ return false;
+
+ node->app_keys = g_list_remove(node->app_keys,
+ GUINT_TO_POINTER(index));
+ /* TODO: remove all associated bindings */
+ return true;
+}
+
+void node_set_primary(struct mesh_node *node, uint16_t unicast)
+{
+ node->primary = unicast;
+}
+
+uint16_t node_get_primary(struct mesh_node *node)
+{
+ if (!node)
+ return UNASSIGNED_ADDRESS;
+ else
+ return node->primary;
+}
+
+void node_set_device_key(struct mesh_node *node, uint8_t *key)
+
+{
+ if (!node || !key)
+ return;
+
+ memcpy(node->dev_key, key, 16);
+}
+
+uint8_t *node_get_device_key(struct mesh_node *node)
+{
+ if (!node)
+ return NULL;
+ else
+ return node->dev_key;
+}
+
+void node_set_num_elements(struct mesh_node *node, uint8_t num_ele)
+{
+ node->num_ele = num_ele;
+}
+
+uint8_t node_get_num_elements(struct mesh_node *node)
+{
+ return node->num_ele;
+}
+
+GList *node_get_net_keys(struct mesh_node *node)
+{
+ if (!node)
+ return NULL;
+ else
+ return node->net_keys;
+}
+
+GList *node_get_app_keys(struct mesh_node *node)
+{
+ if (!node)
+ return NULL;
+ else
+ return node->app_keys;
+}
+
+bool node_parse_composition(struct mesh_node *node, uint8_t *data, uint16_t len)
+{
+ struct mesh_node_composition *comp;
+ uint16_t features;
+ int i;
+
+ comp = g_malloc0(sizeof(struct mesh_node_composition));
+ if (!comp)
+ return false;
+
+ /* skip page -- We only support Page Zero */
+ data++;
+ len--;
+
+ comp->cid = get_le16(&data[0]);
+ comp->pid = get_le16(&data[2]);
+ comp->vid = get_le16(&data[4]);
+ comp->crpl = get_le16(&data[6]);
+ features = get_le16(&data[8]);
+ data += 10;
+ len -= 10;
+
+ comp->relay = !!(features & MESH_FEATURE_RELAY);
+ comp->proxy = !!(features & MESH_FEATURE_PROXY);
+ comp->friend = !!(features & MESH_FEATURE_FRIEND);
+ comp->lpn = !!(features & MESH_FEATURE_LPN);
+
+ for (i = 0; i< node->num_ele; i++) {
+ uint8_t m, v;
+ uint32_t mod_id;
+ uint16_t vendor_id;
+ struct mesh_element *ele;
+ ele = g_malloc0(sizeof(struct mesh_element));
+ if (!ele)
+ return false;
+
+ ele->index = i;
+ ele->loc = get_le16(data);
+ data += 2;
+ node->elements = g_list_append(node->elements, ele);
+
+ m = *data++;
+ v = *data++;
+ len -= 4;
+
+ while (len >= 2 && m--) {
+ mod_id = get_le16(data);
+ /* initialize uppper 16 bits to 0xffff for SIG models */
+ mod_id |= 0xffff0000;
+ if (!node_set_model(node, ele->index, mod_id))
+ return false;
+ data += 2;
+ len -= 2;
+ }
+ while (len >= 4 && v--) {
+ mod_id = get_le16(data + 2);
+ vendor_id = get_le16(data);
+ mod_id |= (vendor_id << 16);
+ if (!node_set_model(node, ele->index, mod_id))
+ return false;
+ data += 4;
+ len -= 4;
+ }
+
+ }
+
+ node->comp = comp;
+ return true;
+}
+
+bool node_set_local_node(struct mesh_node *node)
+{
+ if (local_node) {
+ bt_shell_printf("Local node already registered\n");
+ return false;
+ }
+ net_register_unicast(node->primary, node->num_ele);
+
+ local_node = node;
+ local_node->provisioner = true;
+
+ return true;
+}
+
+struct mesh_node *node_get_local_node()
+{
+ return local_node;
+}
+
+uint16_t node_get_primary_net_idx(struct mesh_node *node)
+{
+ if (node == NULL)
+ return NET_IDX_INVALID;
+
+ return node->primary_net_idx;
+}
+
+static bool deliver_model_data(struct mesh_element* element, uint16_t src,
+ uint16_t app_idx, uint8_t *data, uint16_t len)
+{
+ GList *l;
+
+ for(l = element->models; l; l = l->next) {
+ struct mesh_model *model = l->data;
+
+ if (!g_list_find(model->bindings, GUINT_TO_POINTER(app_idx)))
+ continue;
+
+ if (model->cbs.recv &&
+ model->cbs.recv(src, data, len, model->user_data))
+ return true;
+ }
+
+ return false;
+}
+
+void node_local_data_handler(uint16_t src, uint32_t dst,
+ uint32_t iv_index, uint32_t seq_num,
+ uint16_t app_idx, uint8_t *data, uint16_t len)
+{
+ GList *l;
+ bool res;
+ uint64_t iv_seq;
+ uint64_t iv_seq_remote;
+ uint8_t ele_idx;
+ struct mesh_element *element;
+ struct mesh_node *remote;
+ bool loopback;
+
+ if (!local_node || seq_num > 0xffffff)
+ return;
+
+ iv_seq = iv_index << 24;
+ iv_seq |= seq_num;
+
+ remote = node_find_by_addr(src);
+
+ if (!remote) {
+ if (local_node->provisioner) {
+ bt_shell_printf("Remote node unknown (%4.4x)\n", src);
+ return;
+ }
+
+ remote = g_new0(struct mesh_node, 1);
+ if (!remote)
+ return;
+
+ /* Not Provisioner; Assume all SRC elements stand alone */
+ remote->primary = src;
+ remote->num_ele = 1;
+ nodes = g_list_append(nodes, remote);
+ }
+
+ loopback = (src < (local_node->primary + local_node->num_ele) &&
+ src >= local_node->primary);
+
+ if (!loopback) {
+ iv_seq_remote = remote->iv_index << 24;
+ iv_seq |= remote->seq_number;
+
+ if (iv_seq_remote >= iv_seq) {
+ bt_shell_printf("Replayed message detected "
+ "(%016" PRIx64 " >= %016" PRIx64 ")\n",
+ iv_seq_remote, iv_seq);
+ return;
+ }
+ }
+
+ if (IS_GROUP(dst) || IS_VIRTUAL(dst)) {
+ /* TODO: if subscription address, deliver to subscribers */
+ return;
+ }
+
+ if (IS_ALL_NODES(dst)) {
+ ele_idx = 0;
+ } else {
+ if (dst >= (local_node->primary + local_node->num_ele) ||
+ dst < local_node->primary)
+ return;
+
+ ele_idx = dst - local_node->primary;
+ }
+
+ l = g_list_find_custom(local_node->elements,
+ GUINT_TO_POINTER(ele_idx), match_element_idx);
+
+ /* This should not happen */
+ if (!l)
+ return;
+
+ element = l->data;
+ res = deliver_model_data(element, src, app_idx, data, len);
+
+ if (res && !loopback) {
+ /* TODO: Save remote in Replay Protection db */
+ remote->iv_index = iv_index;
+ remote->seq_number = seq_num;
+ prov_db_node_set_iv_seq(remote, iv_index, seq_num);
+ }
+}
+
+static gboolean restore_model_state(gpointer data)
+{
+ struct mesh_model *model = data;
+ GList *l;
+ struct mesh_model_ops *ops;
+
+ ops = &model->cbs;
+
+ if (model->bindings && ops->bind) {
+ for (l = model->bindings; l; l = l->next) {
+ if (ops->bind(GPOINTER_TO_UINT(l->data), ACTION_ADD) !=
+ MESH_STATUS_SUCCESS)
+ break;
+ }
+ }
+
+ if (model->pub && ops->pub)
+ ops->pub(model->pub);
+
+ g_idle_remove_by_data(data);
+
+ return true;
+
+}
+
+bool node_local_model_register(uint8_t ele_idx, uint16_t model_id,
+ struct mesh_model_ops *ops, void *user_data)
+{
+ uint32_t id = 0xffff0000 | model_id;
+
+ return node_local_vendor_model_register(ele_idx, id, ops, user_data);
+}
+
+bool node_local_vendor_model_register(uint8_t ele_idx, uint32_t model_id,
+ struct mesh_model_ops *ops, void *user_data)
+{
+ struct mesh_element *ele;
+ struct mesh_model *model;
+ GList *l;
+
+ if (!local_node)
+ return false;
+
+ l = g_list_find_custom(local_node->elements, GUINT_TO_POINTER(ele_idx),
+ match_element_idx);
+ if (!l)
+ return false;
+
+ ele = l->data;
+
+ l = g_list_find_custom(ele->models, GUINT_TO_POINTER(model_id),
+ match_model_id);
+ if (!l)
+ return false;
+
+ model = l->data;
+ model->cbs = *ops;
+ model->user_data = user_data;
+
+ if (model_id >= 0xffff0000)
+ model_id = model_id & 0xffff;
+
+ /* Silently assign device key binding to configuration models */
+ if (model_id == CONFIG_SERVER_MODEL_ID ||
+ model_id == CONFIG_CLIENT_MODEL_ID) {
+ model->bindings = g_list_append(model->bindings,
+ GUINT_TO_POINTER(APP_IDX_DEV));
+ } else {
+ g_idle_add(restore_model_state, model);
+ }
+
+ return true;
+}
+
+bool node_set_element(struct mesh_node *node, uint8_t ele_idx)
+{
+ struct mesh_element *ele;
+ GList *l;
+
+ if (!node)
+ return false;
+
+ l = g_list_find_custom(node->elements, GUINT_TO_POINTER(ele_idx),
+ match_element_idx);
+ if (l)
+ return false;
+
+ ele = g_malloc0(sizeof(struct mesh_element));
+ if (!ele)
+ return false;
+
+ ele->index = ele_idx;
+ node->elements = g_list_append(node->elements, ele);
+
+ return true;
+}
+
+bool node_set_model(struct mesh_node *node, uint8_t ele_idx, uint32_t id)
+{
+ struct mesh_element *ele;
+ struct mesh_model *model;
+ GList *l;
+
+ if (!node)
+ return false;
+
+ l = g_list_find_custom(node->elements, GUINT_TO_POINTER(ele_idx),
+ match_element_idx);
+ if (!l)
+ return false;
+
+ ele = l->data;
+
+ l = g_list_find_custom(ele->models, GUINT_TO_POINTER(id),
+ match_model_id);
+ if (l)
+ return false;
+
+ model = g_malloc0(sizeof(struct mesh_model));
+ if (!model)
+ return false;
+
+ model->id = id;
+ ele->models = g_list_append(ele->models, model);
+
+ return true;
+}
+
+bool node_set_composition(struct mesh_node *node,
+ struct mesh_node_composition *comp)
+{
+ if (!node || !comp || node->comp)
+ return false;
+
+ node->comp = g_malloc0(sizeof(struct mesh_node_composition));
+ if (!node->comp)
+ return false;
+
+ *(node->comp) = *comp;
+ return true;
+}
+
+struct mesh_node_composition *node_get_composition(struct mesh_node *node)
+{
+ if (!node)
+ return NULL;
+
+ return node->comp;
+}
+
+static struct mesh_model *get_model(struct mesh_node *node, uint8_t ele_idx,
+ uint32_t model_id)
+{
+ struct mesh_element *ele;
+ GList *l;
+
+ if (!node)
+ return NULL;
+
+ l = g_list_find_custom(node->elements, GUINT_TO_POINTER(ele_idx),
+ match_element_idx);
+ if (!l)
+ return NULL;
+
+ ele = l->data;
+
+ l = g_list_find_custom(ele->models, GUINT_TO_POINTER(model_id),
+ match_model_id);
+ if (!l)
+ return NULL;
+
+ return l->data;
+
+}
+
+bool node_add_binding(struct mesh_node *node, uint8_t ele_idx,
+ uint32_t model_id, uint16_t app_idx)
+{
+ struct mesh_model *model;
+ GList *l;
+
+ model = get_model(node, ele_idx, model_id);
+ if(!model)
+ return false;
+
+ l = g_list_find(model->bindings, GUINT_TO_POINTER(app_idx));
+ if (l)
+ return false;
+
+ if ((node == local_node) && model->cbs.bind) {
+ if (!model->cbs.bind(app_idx, ACTION_ADD))
+ return false;
+ }
+
+ model->bindings = g_list_append(model->bindings,
+ GUINT_TO_POINTER(app_idx));
+
+ return true;
+}
+
+uint8_t node_get_default_ttl(struct mesh_node *node)
+{
+ if (!node)
+ return DEFAULT_TTL;
+ else if (node == local_node)
+ return net_get_default_ttl();
+ else
+ return node->ttl;
+}
+
+bool node_set_default_ttl(struct mesh_node *node, uint8_t ttl)
+{
+ if (!node)
+ return false;
+
+ node->ttl = ttl;
+
+ if (node == local_node || local_node == NULL)
+ return net_set_default_ttl(ttl);
+
+ return true;
+}
+
+bool node_set_sequence_number(struct mesh_node *node, uint32_t seq)
+{
+ if (!node)
+ return false;
+
+ node->seq_number = seq;
+
+ if (node == local_node || local_node == NULL)
+ return net_set_seq_num(seq);
+
+ return true;
+}
+
+uint32_t node_get_sequence_number(struct mesh_node *node)
+{
+ if (!node)
+ return 0xffffffff;
+ else if (node == local_node)
+ return net_get_seq_num();
+
+ return node->seq_number;
+}
+
+bool node_set_iv_index(struct mesh_node *node, uint32_t iv_index)
+{
+ if (!node)
+ return false;
+
+ node->iv_index = iv_index;
+ return true;
+}
+
+uint32_t node_get_iv_index(struct mesh_node *node)
+{
+ bool update;
+
+ if (!node)
+ return 0xffffffff;
+ else if (node == local_node)
+ return net_get_iv_index(&update);
+ return node->iv_index;
+}
+
+bool node_model_pub_set(struct mesh_node *node, uint8_t ele, uint32_t model_id,
+ struct mesh_publication *pub)
+{
+ struct mesh_model *model;
+
+ model = get_model(node, ele, model_id);
+ if(!model)
+ return false;
+
+ if (!model->pub)
+ model->pub = g_malloc0(sizeof(struct mesh_publication));
+ if (!model)
+ return false;
+
+ memcpy(model->pub, pub, (sizeof(struct mesh_publication)));
+
+ if((node == local_node) && model->cbs.pub)
+ model->cbs.pub(pub);
+ return true;
+}
+
+struct mesh_publication *node_model_pub_get(struct mesh_node *node, uint8_t ele,
+ uint32_t model_id)
+{
+ struct mesh_model *model;
+
+ model = get_model(node, ele, model_id);
+ if(!model)
+ return NULL;
+ else
+ return model->pub;
+}
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2017 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
+ *
+ */
+
+struct mesh_node;
+
+#define ACTION_ADD 1
+#define ACTION_UPDATE 2
+#define ACTION_DELETE 3
+
+struct prov_svc_data {
+ uint16_t oob;
+ uint8_t dev_uuid[16];
+};
+
+struct mesh_node_composition {
+ bool relay;
+ bool proxy;
+ bool lpn;
+ bool friend;
+ uint16_t cid;
+ uint16_t pid;
+ uint16_t vid;
+ uint16_t crpl;
+};
+
+struct mesh_publication {
+ uint16_t app_idx;
+ union {
+ uint16_t addr16;
+ uint8_t va_128[16];
+ } u;
+ uint8_t ttl;
+ uint8_t credential;
+ uint8_t period;
+ uint8_t retransmit;
+};
+
+typedef bool (*node_model_recv_callback)(uint16_t src, uint8_t *data,
+ uint16_t len, void *user_data);
+typedef int (*node_model_bind_callback)(uint16_t app_idx, int action);
+typedef void (*node_model_pub_callback)(struct mesh_publication *pub);
+typedef void (*node_model_sub_callback)(uint16_t sub_addr, int action);
+
+struct mesh_model_ops {
+ node_model_recv_callback recv;
+ node_model_bind_callback bind;
+ node_model_pub_callback pub;
+ node_model_sub_callback sub;
+};
+
+struct mesh_node *node_find_by_addr(uint16_t addr);
+struct mesh_node *node_find_by_uuid(uint8_t uuid[16]);
+struct mesh_node *node_create_new(struct prov_svc_data *prov);
+struct mesh_node *node_new(void);
+void node_free(struct mesh_node *node);
+bool node_is_provisioned(struct mesh_node *node);
+void *node_get_prov(struct mesh_node *node);
+void node_set_prov(struct mesh_node *node, void *prov);
+bool node_app_key_add(struct mesh_node *node, uint16_t idx);
+bool node_net_key_add(struct mesh_node *node, uint16_t index);
+bool node_app_key_delete(struct mesh_node *node, uint16_t net_idx,
+ uint16_t idx);
+bool node_net_key_delete(struct mesh_node *node, uint16_t index);
+void node_set_primary(struct mesh_node *node, uint16_t unicast);
+uint16_t node_get_primary(struct mesh_node *node);
+uint16_t node_get_primary_net_idx(struct mesh_node *node);
+void node_set_device_key(struct mesh_node *node, uint8_t *key);
+uint8_t *node_get_device_key(struct mesh_node *node);
+void node_set_num_elements(struct mesh_node *node, uint8_t num_ele);
+uint8_t node_get_num_elements(struct mesh_node *node);
+bool node_parse_composition(struct mesh_node *node, uint8_t *buf, uint16_t len);
+GList *node_get_net_keys(struct mesh_node *node);
+GList *node_get_app_keys(struct mesh_node *node);
+void node_cleanup(void);
+
+bool node_set_local_node(struct mesh_node *node);
+struct mesh_node *node_get_local_node(void);
+void node_local_data_handler(uint16_t src, uint32_t dst,
+ uint32_t iv_index, uint32_t seq_num,
+ uint16_t app_idx, uint8_t *data, uint16_t len);
+
+bool node_local_model_register(uint8_t element_idx, uint16_t model_id,
+ struct mesh_model_ops *ops, void *user_data);
+bool node_local_vendor_model_register(uint8_t element_idx, uint32_t model_id,
+ struct mesh_model_ops *ops, void *user_data);
+
+bool node_set_element(struct mesh_node *node, uint8_t ele_idx);
+bool node_set_model(struct mesh_node *node, uint8_t ele_idx, uint32_t id);
+struct mesh_node_composition *node_get_composition(struct mesh_node *node);
+bool node_set_composition(struct mesh_node *node,
+ struct mesh_node_composition *comp);
+bool node_add_binding(struct mesh_node *node, uint8_t ele_idx,
+ uint32_t model_id, uint16_t app_idx);
+uint8_t node_get_default_ttl(struct mesh_node *node);
+bool node_set_default_ttl(struct mesh_node *node, uint8_t ttl);
+bool node_set_sequence_number(struct mesh_node *node, uint32_t seq);
+uint32_t node_get_sequence_number(struct mesh_node *node);
+bool node_set_iv_index(struct mesh_node *node, uint32_t iv_index);
+uint32_t node_get_iv_index(struct mesh_node *node);
+bool node_model_pub_set(struct mesh_node *node, uint8_t ele, uint32_t model_id,
+ struct mesh_publication *pub);
+struct mesh_publication *node_model_pub_get(struct mesh_node *node, uint8_t ele,
+ uint32_t model_id);
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2017 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>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <sys/uio.h>
+#include <wordexp.h>
+#include <readline/readline.h>
+#include <readline/history.h>
+#include <glib.h>
+
+#include "src/shared/shell.h"
+#include "src/shared/util.h"
+#include "mesh/mesh-net.h"
+#include "mesh/keys.h"
+#include "mesh/net.h"
+#include "mesh/node.h"
+#include "mesh/prov-db.h"
+#include "mesh/util.h"
+#include "mesh/onoff-model.h"
+
+static uint8_t trans_id;
+static uint16_t onoff_app_idx = APP_IDX_INVALID;
+
+static int client_bind(uint16_t app_idx, int action)
+{
+ if (action == ACTION_ADD) {
+ if (onoff_app_idx != APP_IDX_INVALID) {
+ return MESH_STATUS_INSUFF_RESOURCES;
+ } else {
+ onoff_app_idx = app_idx;
+ bt_shell_printf("On/Off client model: new binding %4.4x\n",
+ app_idx);
+ }
+ } else {
+ if (onoff_app_idx == app_idx)
+ onoff_app_idx = APP_IDX_INVALID;
+ }
+ return MESH_STATUS_SUCCESS;
+}
+
+static void print_remaining_time(uint8_t remaining_time)
+{
+ uint8_t step = (remaining_time & 0xc0) >> 6;
+ uint8_t count = remaining_time & 0x3f;
+ int secs = 0, msecs = 0, minutes = 0, hours = 0;
+
+ switch (step) {
+ case 0:
+ msecs = 100 * count;
+ secs = msecs / 1000;
+ msecs -= (secs * 1000);
+ break;
+ case 1:
+ secs = 1 * count;
+ minutes = secs / 60;
+ secs -= (minutes * 60);
+ break;
+
+ case 2:
+ secs = 10 * count;
+ minutes = secs / 60;
+ secs -= (minutes * 60);
+ break;
+ case 3:
+ minutes = 10 * count;
+ hours = minutes / 60;
+ minutes -= (hours * 60);
+ break;
+
+ default:
+ break;
+ }
+
+ bt_shell_printf("\n\t\tRemaining time: %d hrs %d mins %d secs %d msecs\n",
+ hours, minutes, secs, msecs);
+
+}
+
+static bool client_msg_recvd(uint16_t src, uint8_t *data,
+ uint16_t len, void *user_data)
+{
+ uint32_t opcode;
+ int n;
+
+ if (mesh_opcode_get(data, len, &opcode, &n)) {
+ len -= n;
+ data += n;
+ } else
+ return false;
+
+ bt_shell_printf("On Off Model Message received (%d) opcode %x\n",
+ len, opcode);
+ print_byte_array("\t",data, len);
+
+ switch (opcode & ~OP_UNRELIABLE) {
+ default:
+ return false;
+
+ case OP_GENERIC_ONOFF_STATUS:
+ if (len != 1 && len != 3)
+ break;
+
+ bt_shell_printf("Node %4.4x: Off Status present = %s",
+ src, data[0] ? "ON" : "OFF");
+
+ if (len == 3) {
+ bt_shell_printf(", target = %s", data[1] ? "ON" : "OFF");
+ print_remaining_time(data[2]);
+ } else
+ bt_shell_printf("\n");
+ break;
+ }
+
+ return true;
+}
+
+
+static uint32_t target;
+static uint32_t parms[8];
+
+static uint32_t read_input_parameters(int argc, char *argv[])
+{
+ uint32_t i;
+
+ if (!argc)
+ return 0;
+
+ --argc;
+ ++argv;
+
+ if (!argc || argv[0][0] == '\0')
+ return 0;
+
+ memset(parms, 0xff, sizeof(parms));
+
+ for (i = 0; i < sizeof(parms)/sizeof(parms[0]) && i < (unsigned) argc;
+ i++) {
+ sscanf(argv[i], "%x", &parms[i]);
+ if (parms[i] == 0xffffffff)
+ break;
+ }
+
+ return i;
+}
+
+static void cmd_set_node(int argc, char *argv[])
+{
+ uint32_t dst;
+ char *end;
+
+ dst = strtol(argv[1], &end, 16);
+ if (end != (argv[1] + 4)) {
+ bt_shell_printf("Bad unicast address %s: "
+ "expected format 4 digit hex\n", argv[1]);
+ target = UNASSIGNED_ADDRESS;
+ } else {
+ bt_shell_printf("Controlling ON/OFF for node %4.4x\n", dst);
+ target = dst;
+ set_menu_prompt("on/off", argv[1]);
+ }
+}
+
+static bool send_cmd(uint8_t *buf, uint16_t len)
+{
+ struct mesh_node *node = node_get_local_node();
+ uint8_t ttl;
+
+ if(!node)
+ return false;
+
+ ttl = node_get_default_ttl(node);
+
+ return net_access_layer_send(ttl, node_get_primary(node),
+ target, onoff_app_idx, buf, len);
+}
+
+static void cmd_get_status(int argc, char *argv[])
+{
+ uint16_t n;
+ uint8_t msg[32];
+ struct mesh_node *node;
+
+ if (IS_UNASSIGNED(target)) {
+ bt_shell_printf("Destination not set\n");
+ return;
+ }
+
+ node = node_find_by_addr(target);
+
+ if (!node)
+ return;
+
+ n = mesh_opcode_set(OP_GENERIC_ONOFF_GET, msg);
+
+ if (!send_cmd(msg, n))
+ bt_shell_printf("Failed to send \"GENERIC ON/OFF GET\"\n");
+}
+
+static void cmd_set(int argc, char *argv[])
+{
+ uint16_t n;
+ uint8_t msg[32];
+ struct mesh_node *node;
+
+ if (IS_UNASSIGNED(target)) {
+ bt_shell_printf("Destination not set\n");
+ return;
+ }
+
+ node = node_find_by_addr(target);
+
+ if (!node)
+ return;
+
+ if ((read_input_parameters(argc, argv) != 1) &&
+ parms[0] != 0 && parms[0] != 1) {
+ bt_shell_printf("Bad arguments: Expecting \"0\" or \"1\"\n");
+ return;
+ }
+
+ n = mesh_opcode_set(OP_GENERIC_ONOFF_SET, msg);
+ msg[n++] = parms[0];
+ msg[n++] = trans_id++;
+
+ if (!send_cmd(msg, n))
+ bt_shell_printf("Failed to send \"GENERIC ON/OFF SET\"\n");
+
+}
+
+static const struct bt_shell_menu onoff_menu = {
+ .name = "onoff",
+ .desc = "On/Off Model Submenu",
+ .entries = {
+ {"target", "<unicast>", cmd_set_node,
+ "Set node to configure"},
+ {"get", NULL, cmd_get_status,
+ "Get ON/OFF status"},
+ {"onoff", "<0/1>", cmd_set,
+ "Send \"SET ON/OFF\" command"},
+ {} },
+};
+
+static struct mesh_model_ops client_cbs = {
+ client_msg_recvd,
+ client_bind,
+ NULL,
+ NULL
+};
+
+bool onoff_client_init(uint8_t ele)
+{
+ if (!node_local_model_register(ele, GENERIC_ONOFF_CLIENT_MODEL_ID,
+ &client_cbs, NULL))
+ return false;
+
+ bt_shell_add_submenu(&onoff_menu);
+
+ return true;
+}
*
* BlueZ - Bluetooth protocol stack for Linux
*
- * Copyright (C) 2014 Intel Corporation. All rights reserved.
+ * Copyright (C) 2017 Intel Corporation. All rights reserved.
*
*
* This library is free software; you can redistribute it and/or
*
*/
-struct bt_dis;
+#define GENERIC_ONOFF_SERVER_MODEL_ID 0x1000
+#define GENERIC_ONOFF_CLIENT_MODEL_ID 0x1001
-struct bt_dis *bt_dis_new(void *primary);
+#define OP_GENERIC_ONOFF_GET 0x8201
+#define OP_GENERIC_ONOFF_SET 0x8202
+#define OP_GENERIC_ONOFF_SET_UNACK 0x8203
+#define OP_GENERIC_ONOFF_STATUS 0x8204
-struct bt_dis *bt_dis_ref(struct bt_dis *dis);
-void bt_dis_unref(struct bt_dis *dis);
-
-bool bt_dis_attach(struct bt_dis *dis, void *gatt);
-void bt_dis_detach(struct bt_dis *dis);
-
-typedef void (*bt_dis_notify) (uint8_t source, uint16_t vendor,
- uint16_t product, uint16_t version,
- void *user_data);
-
-bool bt_dis_set_notification(struct bt_dis *dis, bt_dis_notify func,
- void *user_data);
+void onoff_set_node(const char *args);
+bool onoff_client_init(uint8_t ele);
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2017 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 <errno.h>
+#include <fcntl.h>
+#include <glib.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <json-c/json.h>
+#include <sys/stat.h>
+
+#include <glib.h>
+
+#include "src/shared/util.h"
+#include "src/shared/shell.h"
+
+#include "mesh/mesh-net.h"
+#include "mesh/crypto.h"
+#include "mesh/keys.h"
+#include "mesh/net.h"
+#include "mesh/node.h"
+#include "mesh/util.h"
+#include "mesh/prov-db.h"
+
+#define CHECK_KEY_IDX_RANGE(x) (((x) >= 0) && ((x) <= 4095))
+
+static const char *prov_filename;
+static const char *local_filename;
+
+static char* prov_file_read(const char *filename)
+{
+ int fd;
+ char *str;
+ struct stat st;
+ ssize_t sz;
+
+ if (!filename)
+ return NULL;
+
+ fd = open(filename,O_RDONLY);
+ if (!fd)
+ return NULL;
+
+ if (fstat(fd, &st) == -1) {
+ close(fd);
+ return NULL;
+ }
+
+ str = (char *) g_malloc0(st.st_size + 1);
+ if (!str) {
+ close(fd);
+ return NULL;
+ }
+
+ sz = read(fd, str, st.st_size);
+ if (sz != st.st_size)
+ bt_shell_printf("Incomplete read: %d vs %d\n", (int)sz,
+ (int)(st.st_size));
+
+ close(fd);
+
+ return str;
+}
+
+static void prov_file_write(json_object *jmain, bool local)
+{
+ FILE *outfile;
+ const char *out_str;
+ const char *out_filename;
+
+ if (local)
+ out_filename = local_filename;
+ else
+ out_filename = prov_filename;
+
+ outfile = fopen(out_filename, "wr");
+ if (!outfile) {
+ bt_shell_printf("Failed to open file %s for writing\n", out_filename);
+ return;
+ }
+
+ out_str = json_object_to_json_string_ext(jmain,
+ JSON_C_TO_STRING_PRETTY);
+
+ fwrite(out_str, sizeof(char), strlen(out_str), outfile);
+ fclose(outfile);
+}
+
+static void put_uint16(json_object *jobject, const char *desc, uint16_t value)
+{
+ json_object *jstring;
+ char buf[5];
+
+ snprintf(buf, 5, "%4.4x", value);
+ jstring = json_object_new_string(buf);
+ json_object_object_add(jobject, desc, jstring);
+}
+
+static void put_uint32(json_object *jobject, const char *desc, uint32_t value)
+{
+ json_object *jstring;
+ char buf[9];
+
+ snprintf(buf, 9, "%8.8x", value);
+ jstring = json_object_new_string(buf);
+ json_object_object_add(jobject, desc, jstring);
+}
+
+static void put_uint16_array_entry(json_object *jarray, uint16_t value)
+{
+ json_object *jstring;
+ char buf[5];
+
+ snprintf(buf, 5, "%4.4x", value);
+ jstring = json_object_new_string(buf);
+ json_object_array_add(jarray, jstring);
+}
+
+static void put_uint32_array_entry(json_object *jarray, uint32_t value)
+{
+ json_object *jstring;
+ char buf[9];
+
+ snprintf(buf, 9, "%8.8x", value);
+ jstring = json_object_new_string(buf);
+ json_object_array_add(jarray, jstring);
+}
+
+static void put_uint16_list(json_object *jarray, GList *list)
+{
+ GList *l;
+
+ if (!list)
+ return;
+
+ for (l = list; l; l = l->next) {
+ uint32_t ivalue = GPOINTER_TO_UINT(l->data);
+ put_uint16_array_entry(jarray, ivalue);
+ }
+}
+
+static void add_node_idxs(json_object *jnode, const char *desc,
+ GList *idxs)
+{
+ json_object *jarray;
+
+ jarray = json_object_new_array();
+
+ put_uint16_list(jarray, idxs);
+
+ json_object_object_add(jnode, desc, jarray);
+}
+
+static bool parse_unicast_range(json_object *jobject)
+{
+ int cnt;
+ int i;
+
+ cnt = json_object_array_length(jobject);
+
+ for (i = 0; i < cnt; ++i) {
+ json_object *jrange;
+ json_object *jvalue;
+ uint16_t low, high;
+ char *str;
+
+ jrange = json_object_array_get_idx(jobject, i);
+ json_object_object_get_ex(jrange, "lowAddress", &jvalue);
+ str = (char *)json_object_get_string(jvalue);
+ if (sscanf(str, "%04hx", &low) != 1)
+ return false;
+
+ json_object_object_get_ex(jrange, "highAddress", &jvalue);
+ str = (char *)json_object_get_string(jvalue);
+ if (sscanf(str, "%04hx", &high) != 1)
+ return false;
+
+ if(high < low)
+ return false;
+
+ net_add_address_pool(low, high);
+ }
+ return true;
+}
+
+static int parse_node_keys(struct mesh_node *node, json_object *jidxs,
+ bool is_app_key)
+{
+ int idx_cnt;
+ int i;
+
+ idx_cnt = json_object_array_length(jidxs);
+ for (i = 0; i < idx_cnt; ++i) {
+ int idx;
+ json_object *jvalue;
+
+ jvalue = json_object_array_get_idx(jidxs, i);
+ if (!jvalue)
+ break;
+ idx = json_object_get_int(jvalue);
+ if (!CHECK_KEY_IDX_RANGE(idx))
+ break;
+
+ if (is_app_key)
+ node_app_key_add(node, idx);
+ else
+ node_net_key_add(node, idx);
+ }
+
+ return i;
+}
+
+static bool parse_composition_models(struct mesh_node *node, int index,
+ json_object *jmodels)
+{
+ int model_cnt;
+ int i;
+
+ model_cnt = json_object_array_length(jmodels);
+
+ for (i = 0; i < model_cnt; ++i) {
+ json_object *jmodel;
+ char *str;
+ uint32_t model_id;
+ int len;
+
+ jmodel = json_object_array_get_idx(jmodels, i);
+ str = (char *)json_object_get_string(jmodel);
+ len = strlen(str);
+
+ if (len != 4 && len != 8)
+ return false;
+
+ if (sscanf(str, "%08x", &model_id) != 1)
+ return false;
+ if (len == 4)
+ model_id += 0xffff0000;
+
+ node_set_model(node, index, model_id);
+ }
+
+ return true;
+}
+
+static bool parse_composition_elements(struct mesh_node *node,
+ json_object *jelements)
+{
+ int el_cnt;
+ int i;
+
+ el_cnt = json_object_array_length(jelements);
+ node_set_num_elements(node, el_cnt);
+
+ for (i = 0; i < el_cnt; ++i) {
+ json_object *jelement;
+ json_object *jmodels;
+ json_object *jvalue;
+ int index;
+
+ jelement = json_object_array_get_idx(jelements, i);
+ json_object_object_get_ex(jelement, "elementIndex", &jvalue);
+ if (jvalue) {
+ index = json_object_get_int(jvalue);
+ if (index >= el_cnt) {
+ return false;
+ }
+ } else
+ return false;
+
+ if (!node_set_element(node, index))
+ return false;
+
+ json_object_object_get_ex(jelement, "models", &jmodels);
+ if (!jmodels)
+ continue;
+
+ if(!parse_composition_models(node, index, jmodels))
+ return false;
+ }
+ return true;
+}
+
+static bool parse_model_pub(struct mesh_node *node, int ele_idx,
+ uint32_t model_id, json_object *jpub)
+{
+ json_object *jvalue;
+ struct mesh_publication pub;
+ char *str;
+
+ memset(&pub, 0, sizeof(struct mesh_publication));
+
+ /* Read only required fields */
+ json_object_object_get_ex(jpub, "address", &jvalue);
+ if (!jvalue)
+ return false;
+
+ str = (char *)json_object_get_string(jvalue);
+ if (sscanf(str, "%04hx", &pub.u.addr16) != 1)
+ return false;
+
+ json_object_object_get_ex(jpub, "index", &jvalue);
+ if (!jvalue)
+ return false;
+
+ str = (char *)json_object_get_string(jvalue);
+ if (sscanf(str, "%04hx", &pub.app_idx) != 1)
+ return false;
+
+
+ json_object_object_get_ex(jpub, "ttl", &jvalue);
+ pub.ttl = json_object_get_int(jvalue);
+
+ if (!node_model_pub_set(node, ele_idx, model_id, &pub))
+ return false;
+
+ return true;
+}
+
+static bool parse_bindings(struct mesh_node *node, int ele_idx,
+ uint32_t model_id, json_object *jbindings)
+{
+ int cnt;
+ int i;
+
+ cnt = json_object_array_length(jbindings);
+
+ for (i = 0; i < cnt; ++i) {
+ int key_idx;
+ json_object *jvalue;
+
+ jvalue = json_object_array_get_idx(jbindings, i);
+ if (!jvalue)
+ return true;
+
+ key_idx = json_object_get_int(jvalue);
+ if (!CHECK_KEY_IDX_RANGE(key_idx))
+ return false;
+
+ if (!node_add_binding(node, ele_idx, model_id, key_idx))
+ return false;
+ }
+
+ return true;
+}
+
+static json_object* find_configured_model(struct mesh_node *node, int ele_idx,
+ json_object *jmodels, uint32_t target_id)
+{
+ int model_cnt;
+ int i;
+
+ model_cnt = json_object_array_length(jmodels);
+
+ for (i = 0; i < model_cnt; ++i) {
+ json_object *jmodel;
+ json_object *jvalue;
+ char *str;
+ int len;
+ uint32_t model_id;
+
+ jmodel = json_object_array_get_idx(jmodels, i);
+
+ json_object_object_get_ex(jmodel, "modelId", &jvalue);
+ str = (char *)json_object_get_string(jvalue);
+
+ len = strlen(str);
+
+ if (len != 4 && len != 8)
+ return NULL;
+
+ if (sscanf(str, "%08x", &model_id) != 1)
+ return NULL;
+
+ if (len == 4)
+ model_id += 0xffff0000;
+
+ if (model_id == target_id)
+ return jmodel;
+ }
+
+ return NULL;
+}
+
+static bool parse_configuration_models(struct mesh_node *node, int ele_idx,
+ json_object *jmodels)
+{
+ int model_cnt;
+ int i;
+
+ model_cnt = json_object_array_length(jmodels);
+
+ for (i = 0; i < model_cnt; ++i) {
+ json_object *jmodel;
+ json_object *jvalue;
+ json_object *jarray;
+ char *str;
+ int len;
+ uint32_t model_id;
+
+ jmodel = json_object_array_get_idx(jmodels, i);
+
+ json_object_object_get_ex(jmodel, "modelId", &jvalue);
+ str = (char *)json_object_get_string(jvalue);
+
+ len = strlen(str);
+
+ if (len != 4 && len != 8)
+ return false;
+
+ if (sscanf(str, "%08x", &model_id) != 1)
+ return false;
+ if (len == 4)
+ model_id += 0xffff0000;
+
+ json_object_object_get_ex(jmodel, "bind", &jarray);
+ if (jarray && !parse_bindings(node, ele_idx, model_id, jarray))
+ return false;
+
+ json_object_object_get_ex(jmodel, "publish", &jvalue);
+
+ if (jvalue && !parse_model_pub(node, ele_idx, model_id, jvalue))
+ return false;
+ }
+
+ return true;
+}
+
+static bool parse_configuration_elements(struct mesh_node *node,
+ json_object *jelements, bool local)
+{
+ int el_cnt;
+ int i;
+
+ el_cnt = json_object_array_length(jelements);
+ node_set_num_elements(node, el_cnt);
+
+ for (i = 0; i < el_cnt; ++i) {
+ json_object *jelement;
+ json_object *jmodels;
+ json_object *jvalue;
+ int index;
+ uint16_t addr;
+
+ jelement = json_object_array_get_idx(jelements, i);
+ json_object_object_get_ex(jelement, "elementIndex", &jvalue);
+ if (jvalue) {
+ index = json_object_get_int(jvalue);
+ if (index >= el_cnt) {
+ return false;
+ }
+ } else
+ return false;
+
+ if (index == 0) {
+ char *str;
+
+ json_object_object_get_ex(jelement, "unicastAddress",
+ &jvalue);
+ str = (char *)json_object_get_string(jvalue);
+ if (sscanf(str, "%04hx", &addr) != 1)
+ return false;
+
+ if (!local && !net_reserve_address_range(addr, el_cnt))
+ return false;
+
+ node_set_primary(node, addr);
+ }
+
+ json_object_object_get_ex(jelement, "models", &jmodels);
+ if (!jmodels)
+ continue;
+
+ if(!parse_configuration_models(node, index, jmodels))
+ return false;
+ }
+ return true;
+}
+
+static void add_key(json_object *jobject, const char *desc, uint8_t* key)
+{
+ json_object *jstring;
+ char hexstr[33];
+
+ hex2str(key, 16, hexstr, 33);
+ jstring = json_object_new_string(hexstr);
+ json_object_object_add(jobject, desc, jstring);
+}
+
+static json_object *find_node_by_primary(json_object *jmain, uint16_t primary)
+{
+ json_object *jarray;
+ int i, len;
+
+ json_object_object_get_ex(jmain, "nodes", &jarray);
+
+ if (!jarray)
+ return NULL;
+ len = json_object_array_length(jarray);
+
+ for (i = 0; i < len; ++i) {
+ json_object *jnode;
+ json_object *jconfig;
+ json_object *jelements;
+ json_object *jelement;
+ json_object *jvalue;
+ char *str;
+ uint16_t addr;
+
+ jnode = json_object_array_get_idx(jarray, i);
+ if (!jnode)
+ return NULL;
+
+ json_object_object_get_ex(jnode, "configuration", &jconfig);
+ if (!jconfig)
+ return NULL;
+
+ json_object_object_get_ex(jconfig, "elements", &jelements);
+ if (!jelements)
+ return NULL;
+
+ jelement = json_object_array_get_idx(jelements, 0);
+ if (!jelement)
+ return NULL;
+
+ json_object_object_get_ex(jelement, "unicastAddress",
+ &jvalue);
+ str = (char *)json_object_get_string(jvalue);
+ if (sscanf(str, "%04hx", &addr) != 1)
+ return NULL;
+
+ if (addr == primary)
+ return jnode;
+ }
+
+ return NULL;
+
+}
+
+void prov_db_print_node_composition(struct mesh_node *node)
+{
+ char *in_str;
+ const char *comp_str;
+ json_object *jmain;
+ json_object *jnode;
+ json_object *jcomp;
+ uint16_t primary = node_get_primary(node);
+ const char *filename;
+ bool res = false;
+
+ if (!node || !node_get_composition(node))
+ return;
+
+ if (node == node_get_local_node())
+ filename = local_filename;
+ else
+ filename = prov_filename;
+
+ in_str = prov_file_read(filename);
+ if (!in_str)
+ return;
+
+ jmain = json_tokener_parse(in_str);
+ if (!jmain)
+ goto done;
+
+ jnode = find_node_by_primary(jmain, primary);
+ if (!jnode)
+ goto done;
+
+ json_object_object_get_ex(jnode, "composition", &jcomp);
+ if (!jcomp)
+ goto done;
+
+ comp_str = json_object_to_json_string_ext(jcomp,
+ JSON_C_TO_STRING_PRETTY);
+
+ res = true;
+
+done:
+ if (res)
+ bt_shell_printf("\tComposition data for node %4.4x %s\n",
+ primary, comp_str);
+ else
+ bt_shell_printf("\tComposition data for node %4.4x not present\n",
+ primary);
+ g_free(in_str);
+
+ if (jmain)
+ json_object_put(jmain);
+}
+
+bool prov_db_add_node_composition(struct mesh_node *node, uint8_t *data,
+ uint16_t len)
+{
+ char *in_str;
+ json_object *jmain;
+ json_object *jnode;
+ json_object *jcomp;
+ json_object *jbool;
+ json_object *jfeatures;
+ json_object *jelements;
+ struct mesh_node_composition *comp;
+ uint8_t num_ele;
+ int i;
+ uint16_t primary = node_get_primary(node);
+ bool res = NULL;
+
+ comp = node_get_composition(node);
+ if (!comp)
+ return false;
+
+ in_str = prov_file_read(prov_filename);
+ if (!in_str)
+ return false;
+
+ jmain = json_tokener_parse(in_str);
+ if (!jmain)
+ goto done;
+
+ jnode = find_node_by_primary(jmain, primary);
+ if (!jnode)
+ goto done;
+
+ jcomp = json_object_new_object();
+
+ put_uint16(jcomp, "cid", comp->cid);
+ put_uint16(jcomp, "pid", comp->pid);
+ put_uint16(jcomp, "vid", comp->pid);
+ put_uint16(jcomp, "crpl", comp->crpl);
+
+ jfeatures = json_object_new_object();
+ jbool = json_object_new_boolean(comp->relay);
+ json_object_object_add(jfeatures, "relay", jbool);
+ jbool = json_object_new_boolean(comp->proxy);
+ json_object_object_add(jfeatures, "proxy", jbool);
+ jbool = json_object_new_boolean(comp->friend);
+ json_object_object_add(jfeatures, "friend", jbool);
+ jbool = json_object_new_boolean(comp->lpn);
+ json_object_object_add(jfeatures, "lpn", jbool);
+ json_object_object_add(jcomp, "features", jfeatures);
+
+ data += 11;
+ len -= 11;
+
+ num_ele = node_get_num_elements(node);
+
+ jelements = json_object_new_array();
+
+ for (i = 0; i < num_ele; ++i) {
+ json_object *jelement;
+ json_object *jmodels;
+ json_object *jint;
+ uint32_t mod_id;
+ uint16_t vendor_id;
+ uint8_t m, v;
+
+ jelement = json_object_new_object();
+
+ /* Element Index */
+ jint = json_object_new_int(i);
+ json_object_object_add(jelement, "elementIndex", jint);
+
+ /* Location */
+ put_uint16(jelement, "location", get_le16(data));
+ data += 2;
+ m = *data++;
+ v = *data++;
+ len -= 4;
+
+ /* Models */
+ jmodels = json_object_new_array();
+ while (len >= 2 && m--) {
+ mod_id = get_le16(data);
+ data += 2;
+ len -= 2;
+ put_uint16_array_entry(jmodels, (uint16_t) mod_id);
+ }
+
+ while (len >= 4 && v--) {
+ mod_id = get_le16(data + 2);
+ vendor_id = get_le16(data);
+ mod_id |= (vendor_id << 16);
+ data += 4;
+ len -= 4;
+ put_uint32_array_entry(jmodels, mod_id);
+ }
+
+ json_object_object_add(jelement, "models", jmodels);
+ json_object_array_add(jelements, jelement);
+ }
+
+ json_object_object_add(jcomp, "elements", jelements);
+
+ json_object_object_add(jnode, "composition", jcomp);
+
+ prov_file_write(jmain, false);
+
+ res = true;;
+done:
+
+ g_free(in_str);
+
+ if(jmain)
+ json_object_put(jmain);
+
+ return res;
+}
+
+bool prov_db_node_set_ttl(struct mesh_node *node, uint8_t ttl)
+{
+ char *in_str;
+ json_object *jmain;
+ json_object *jnode;
+ json_object *jconfig;
+ json_object *jvalue;
+ uint16_t primary = node_get_primary(node);
+ const char *filename;
+ bool local = node == node_get_local_node();
+ bool res = false;
+
+ if (local)
+ filename = local_filename;
+ else
+ filename = prov_filename;
+
+ in_str = prov_file_read(filename);
+ if (!in_str)
+ return false;
+
+ jmain = json_tokener_parse(in_str);
+ if (!jmain)
+ goto done;
+
+ if (local)
+ json_object_object_get_ex(jmain, "node", &jnode);
+ else
+ jnode = find_node_by_primary(jmain, primary);
+
+ if (!jnode)
+ goto done;
+
+ json_object_object_get_ex(jnode, "configuration", &jconfig);
+ if (!jconfig)
+ goto done;
+
+ json_object_object_del(jconfig, "defaultTTL");
+
+ jvalue = json_object_new_int(ttl);
+ json_object_object_add(jconfig, "defaultTTL", jvalue);
+
+ prov_file_write(jmain, local);
+
+ res = true;
+done:
+
+ g_free(in_str);
+
+ if(jmain)
+ json_object_put(jmain);
+
+ return res;
+
+}
+
+static void set_local_iv_index(json_object *jobj, uint32_t idx, bool update)
+{
+ json_object *jvalue;
+
+ json_object_object_del(jobj, "IVindex");
+ jvalue = json_object_new_int(idx);
+ json_object_object_add(jobj, "IVindex", jvalue);
+
+ json_object_object_del(jobj, "IVupdate");
+ jvalue = json_object_new_int((update) ? 1 : 0);
+ json_object_object_add(jobj, "IVupdate", jvalue);
+
+}
+
+bool prov_db_local_set_iv_index(uint32_t iv_index, bool update, bool prov)
+{
+ char *in_str;
+ json_object *jmain;
+ json_object *jnode;
+ bool res = false;
+
+ in_str = prov_file_read(local_filename);
+ if (!in_str)
+ return false;
+
+ jmain = json_tokener_parse(in_str);
+ if (!jmain)
+ goto done;
+
+ json_object_object_get_ex(jmain, "node", &jnode);
+ set_local_iv_index(jnode, iv_index, update);
+ prov_file_write(jmain, true);
+
+ g_free(in_str);
+ json_object_put(jmain);
+
+ /* If provisioner, save to global DB as well */
+ if (prov) {
+ in_str = prov_file_read(prov_filename);
+ if (!in_str)
+ return false;
+
+ jmain = json_tokener_parse(in_str);
+ if (!jmain)
+ goto done;
+
+ set_local_iv_index(jmain, iv_index, update);
+ prov_file_write(jmain, false);
+ }
+
+ res = true;
+done:
+
+ g_free(in_str);
+
+ if(jmain)
+ json_object_put(jmain);
+
+ return res;
+
+}
+
+bool prov_db_local_set_seq_num(uint32_t seq_num)
+{
+ char *in_str;
+ json_object *jmain;
+ json_object *jnode;
+ json_object *jvalue;
+ bool res = false;
+
+ in_str = prov_file_read(local_filename);
+ if (!in_str)
+ return false;
+
+ jmain = json_tokener_parse(in_str);
+ if (!jmain)
+ goto done;
+
+ json_object_object_get_ex(jmain, "node", &jnode);
+
+ json_object_object_del(jnode, "sequenceNumber");
+ jvalue = json_object_new_int(seq_num);
+ json_object_object_add(jnode, "sequenceNumber", jvalue);
+
+ prov_file_write(jmain, true);
+
+ res = true;
+done:
+
+ g_free(in_str);
+
+ if(jmain)
+ json_object_put(jmain);
+
+ return res;
+}
+
+bool prov_db_node_set_iv_seq(struct mesh_node *node, uint32_t iv, uint32_t seq)
+{
+ char *in_str;
+ json_object *jmain;
+ json_object *jnode;
+ json_object *jvalue;
+ uint16_t primary = node_get_primary(node);
+ bool res = false;
+
+ in_str = prov_file_read(prov_filename);
+ if (!in_str)
+ return false;
+
+ jmain = json_tokener_parse(in_str);
+ if (!jmain)
+ goto done;
+
+ jnode = find_node_by_primary(jmain, primary);
+ if (!jnode)
+ goto done;
+
+ json_object_object_del(jnode, "IVindex");
+
+ jvalue = json_object_new_int(iv);
+ json_object_object_add(jnode, "IVindex", jvalue);
+
+ json_object_object_del(jnode, "sequenceNumber");
+
+ jvalue = json_object_new_int(seq);
+ json_object_object_add(jnode, "sequenceNumber", jvalue);
+
+ prov_file_write(jmain, false);
+
+ res = true;
+done:
+
+ g_free(in_str);
+
+ if(jmain)
+ json_object_put(jmain);
+
+ return res;
+
+}
+
+bool prov_db_node_keys(struct mesh_node *node, GList *idxs, const char *desc)
+{
+ char *in_str;
+ json_object *jmain;
+ json_object *jnode;
+ json_object *jconfig;
+ json_object *jidxs;
+ uint16_t primary = node_get_primary(node);
+ const char *filename;
+ bool local = (node == node_get_local_node());
+ bool res = false;
+
+ if (local)
+ filename = local_filename;
+ else
+ filename = prov_filename;
+
+ in_str = prov_file_read(filename);
+ if (!in_str)
+ return false;
+
+ jmain = json_tokener_parse(in_str);
+ if (!jmain)
+ goto done;
+
+ jnode = find_node_by_primary(jmain, primary);
+ if (!jnode)
+ goto done;
+
+ json_object_object_get_ex(jnode, "configuration", &jconfig);
+ if (!jconfig)
+ goto done;
+
+ json_object_object_del(jconfig, desc);
+
+ if (idxs) {
+ jidxs = json_object_new_array();
+ put_uint16_list(jidxs, idxs);
+ json_object_object_add(jconfig, desc, jidxs);
+ }
+
+ prov_file_write(jmain, local);
+
+ res = true;
+done:
+
+ g_free(in_str);
+
+ if(jmain)
+ json_object_put(jmain);
+
+ return res;
+
+}
+
+static json_object *get_jmodel_obj(struct mesh_node *node, uint8_t ele_idx,
+ uint32_t model_id, json_object **jmain)
+{
+ char *in_str;
+ json_object *jnode;
+ json_object *jconfig;
+ json_object *jelements, *jelement;
+ json_object *jmodels, *jmodel = NULL;
+ uint16_t primary = node_get_primary(node);
+ const char *filename;
+ bool local = (node == node_get_local_node());
+
+ if (local)
+ filename = local_filename;
+ else
+ filename = prov_filename;
+
+ in_str = prov_file_read(filename);
+ if (!in_str)
+ return NULL;
+
+ *jmain = json_tokener_parse(in_str);
+ if (!(*jmain))
+ goto done;
+
+ if (local)
+ json_object_object_get_ex(*jmain, "node", &jnode);
+ else
+ jnode = find_node_by_primary(*jmain, primary);
+
+ if (!jnode)
+ goto done;
+
+ /* Configuration is mandatory for nodes in provisioning database */
+ json_object_object_get_ex(jnode, "configuration", &jconfig);
+ if (!jconfig)
+ goto done;
+
+ json_object_object_get_ex(jconfig, "elements", &jelements);
+ if (!jelements) {
+ goto done;
+ }
+
+ jelement = json_object_array_get_idx(jelements, ele_idx);
+ if (!jelement) {
+ goto done;
+ }
+
+ json_object_object_get_ex(jelement, "models", &jmodels);
+
+ if (!jmodels) {
+ jmodels = json_object_new_array();
+ json_object_object_add(jelement, "models", jmodels);
+ } else {
+ jmodel = find_configured_model(node, ele_idx, jmodels,
+ model_id);
+ }
+
+ if (!jmodel) {
+ jmodel = json_object_new_object();
+
+ if ((model_id & 0xffff0000) == 0xffff0000)
+ put_uint16(jmodel, "modelId", model_id & 0xffff);
+ else
+ put_uint32(jmodel, "modelId", model_id);
+
+ json_object_array_add(jmodels, jmodel);
+ }
+
+done:
+
+ g_free(in_str);
+
+ if(!jmodel && *jmain)
+ json_object_put(*jmain);
+
+ return jmodel;
+
+}
+
+bool prov_db_add_binding(struct mesh_node *node, uint8_t ele_idx,
+ uint32_t model_id, uint16_t app_idx)
+{
+ json_object *jmain;
+ json_object *jmodel;
+ json_object *jvalue;
+ json_object *jbindings = NULL;
+ bool local = (node == node_get_local_node());
+
+ jmodel = get_jmodel_obj(node, ele_idx, model_id, &jmain);
+
+ if (!jmodel)
+ return false;
+
+ json_object_object_get_ex(jmodel, "bind", &jbindings);
+
+ if (!jbindings) {
+ jbindings = json_object_new_array();
+ json_object_object_add(jmodel, "bind", jbindings);
+ }
+
+ jvalue = json_object_new_int(app_idx);
+ json_object_array_add(jbindings, jvalue);
+
+ prov_file_write(jmain, local);
+
+ json_object_put(jmain);
+
+ return true;
+}
+
+bool prov_db_node_set_model_pub(struct mesh_node *node, uint8_t ele_idx,
+ uint32_t model_id,
+ struct mesh_publication *pub)
+{
+ json_object *jmain;
+ json_object *jmodel;
+ json_object *jpub;
+ json_object *jvalue;
+ bool local = (node == node_get_local_node());
+
+ jmodel = get_jmodel_obj(node, ele_idx, model_id, &jmain);
+
+ if (!jmodel)
+ return false;
+
+ json_object_object_del(jmodel, "publish");
+ if (!pub)
+ goto done;
+
+ jpub = json_object_new_object();
+
+ /* Save only required fields */
+ put_uint16(jpub, "address", pub->u.addr16);
+ put_uint16(jpub, "index", pub->app_idx);
+ jvalue = json_object_new_int(pub->ttl);
+ json_object_object_add(jpub, "ttl", jvalue);
+
+ json_object_object_add(jmodel, "publish", jpub);
+
+done:
+ prov_file_write(jmain, local);
+
+ json_object_put(jmain);
+
+ return true;
+}
+
+bool prov_db_add_new_node(struct mesh_node *node)
+{
+ char *in_str;
+ json_object *jmain;
+ json_object *jarray;
+ json_object *jnode;
+ json_object *jconfig;
+ json_object *jelements;
+ uint8_t num_ele;
+ uint16_t primary;
+ int i;
+ bool first_node;
+ bool res = false;
+
+ in_str = prov_file_read(prov_filename);
+ if (!in_str)
+ return false;
+
+ jmain = json_tokener_parse(in_str);
+ if (!jmain)
+ goto done;
+ json_object_object_get_ex(jmain, "nodes", &jarray);
+
+ if (!jarray) {
+ jarray = json_object_new_array();
+ first_node = true;
+ } else
+ first_node = false;
+
+ jnode = json_object_new_object();
+
+ /* Device key */
+ add_key(jnode, "deviceKey", node_get_device_key(node));
+
+ /* Net key */
+ jconfig = json_object_new_object();
+ add_node_idxs(jconfig, "netKeys", node_get_net_keys(node));
+
+ num_ele = node_get_num_elements(node);
+ if (num_ele == 0)
+ goto done;
+
+ jelements = json_object_new_array();
+
+ primary = node_get_primary(node);
+ if (IS_UNASSIGNED(primary))
+ goto done;
+
+ for (i = 0; i < num_ele; ++i) {
+ json_object *jelement;
+ json_object *jint;
+
+ jelement = json_object_new_object();
+
+ /* Element Index */
+ jint = json_object_new_int(i);
+ json_object_object_add(jelement, "elementIndex", jint);
+
+ /* Unicast */
+ put_uint16(jelement, "unicastAddress", primary + i);
+
+ json_object_array_add(jelements, jelement);
+ }
+
+ json_object_object_add(jconfig, "elements", jelements);
+
+ json_object_object_add(jnode, "configuration", jconfig);
+
+ json_object_array_add(jarray, jnode);
+
+ if (first_node)
+ json_object_object_add(jmain, "nodes", jarray);
+
+ prov_file_write(jmain, false);
+
+ res = true;
+done:
+
+ g_free(in_str);
+
+ if (jmain)
+ json_object_put(jmain);
+
+ return res;
+}
+
+static bool parse_node_composition(struct mesh_node *node, json_object *jcomp)
+{
+ json_object *jvalue;
+ json_object *jelements;
+ json_bool enable;
+ char *str;
+ struct mesh_node_composition comp;
+
+ json_object_object_get_ex(jcomp, "cid", &jvalue);
+ if (!jvalue)
+ return false;
+
+ str = (char *)json_object_get_string(jvalue);
+
+ if (sscanf(str, "%04hx", &comp.cid) != 1)
+ return false;
+
+ json_object_object_get_ex(jcomp, "pid", &jvalue);
+ if (!jvalue)
+ return false;
+
+ str = (char *)json_object_get_string(jvalue);
+
+ if (sscanf(str, "%04hx", &comp.vid) != 1)
+ return false;
+
+ json_object_object_get_ex(jcomp, "vid", &jvalue);
+ if (!jvalue)
+ return false;
+
+ str = (char *)json_object_get_string(jvalue);
+
+ if (sscanf(str, "%04hx", &comp.vid) != 1)
+ return false;
+
+ json_object_object_get_ex(jcomp, "crpl", &jvalue);
+ if (!jvalue)
+ return false;
+
+ str = (char *)json_object_get_string(jvalue);
+
+ if (sscanf(str, "%04hx", &comp.crpl) != 1)
+ return false;
+
+ /* Extract features */
+ json_object_object_get_ex(jcomp, "relay", &jvalue);
+ enable = json_object_get_boolean(jvalue);
+ comp.relay = (enable) ? true : false;
+
+ json_object_object_get_ex(jcomp, "proxy", &jvalue);
+ enable = json_object_get_boolean(jvalue);
+ comp.proxy = (enable) ? true : false;
+
+ json_object_object_get_ex(jcomp, "friend", &jvalue);
+ enable = json_object_get_boolean(jvalue);
+ comp.friend = (enable) ? true : false;
+
+ json_object_object_get_ex(jcomp, "lowPower", &jvalue);
+ enable = json_object_get_boolean(jvalue);
+ comp.lpn = (enable) ? true : false;
+
+ if (!node_set_composition(node, &comp))
+ return false;
+
+ json_object_object_get_ex(jcomp, "elements", &jelements);
+ if (!jelements)
+ return false;
+
+ return parse_composition_elements(node, jelements);
+}
+
+static bool parse_node(json_object *jnode, bool local)
+{
+ json_object *jconfig;
+ json_object *jelements;
+ json_object *jidxs;
+ json_object *jvalue;
+ json_object *jint;
+ uint8_t key[16];
+ char *value_str;
+ uint32_t idx;
+ struct mesh_node *node;
+
+ /* Device key */
+ if (!json_object_object_get_ex(jnode, "deviceKey", &jvalue) ||
+ !jvalue) {
+ if (!mesh_get_random_bytes(key, 16))
+ return false;
+
+ add_key(jnode, "deviceKey", key);
+ } else {
+ value_str = (char *)json_object_get_string(jvalue);
+ if (!str2hex(value_str, strlen(value_str), key, 16))
+ return false;;
+ }
+
+ node = node_new();
+
+ if (!node)
+ return false;
+
+ node_set_device_key(node, key);
+
+ json_object_object_get_ex(jnode, "IVindex", &jint);
+ if (jint)
+ idx = json_object_get_int(jint);
+ else
+ idx = 0;
+
+ node_set_iv_index(node, idx);
+ if (local) {
+ bool update = false;
+ json_object_object_get_ex(jnode, "IVupdate", &jint);
+ if (jint)
+ update = json_object_get_int(jint) ? true : false;
+ net_set_iv_index(idx, update);
+ }
+
+ if (json_object_object_get_ex(jnode, "sequenceNumber", &jint) &&
+ jint) {
+ int seq = json_object_get_int(jint);
+ node_set_sequence_number(node, seq);
+ }
+
+ /* Composition is mandatory for local node */
+ json_object_object_get_ex(jnode, "composition", &jconfig);
+ if ((jconfig && !parse_node_composition(node, jconfig)) ||
+ (!jconfig && local)) {
+ node_free(node);
+ return false;
+ }
+
+ /* Configuration is mandatory for nodes in provisioning database */
+ json_object_object_get_ex(jnode, "configuration", &jconfig);
+ if (!jconfig) {
+ if (local) {
+ /* This is an unprovisioned local device */
+ goto done;
+ } else {
+ node_free(node);
+ return false;
+ }
+ }
+
+ json_object_object_get_ex(jconfig, "elements", &jelements);
+ if (!jelements) {
+ node_free(node);
+ return false;
+ }
+
+ if (!parse_configuration_elements(node, jelements, local)) {
+ node_free(node);
+ return false;;
+ }
+
+ json_object_object_get_ex(jconfig, "netKeys", &jidxs);
+ if (!jidxs || (parse_node_keys(node, jidxs, false) == 0)) {
+ node_free(node);
+ return false;
+ }
+
+ json_object_object_get_ex(jconfig, "appKeys", &jidxs);
+ if (jidxs)
+ parse_node_keys(node, jidxs, true);
+
+ json_object_object_get_ex(jconfig, "defaultTTL", &jvalue);
+ if (jvalue) {
+ int ttl = json_object_get_int(jvalue);
+ node_set_default_ttl(node, ttl &TTL_MASK);
+ }
+
+done:
+ if (local && !node_set_local_node(node)) {
+ node_free(node);
+ return false;
+ }
+
+ return true;
+}
+
+bool prov_db_show(const char *filename)
+{
+ char *str;
+
+ str = prov_file_read(filename);
+ if (!str)
+ return false;
+
+ bt_shell_printf("%s\n", str);
+ g_free(str);
+ return true;
+}
+
+static bool read_json_db(const char *filename, bool provisioner, bool local)
+{
+ char *str;
+ json_object *jmain;
+ json_object *jarray;
+ json_object *jprov;
+ json_object *jvalue;
+ json_object *jtemp;
+ uint8_t key[16];
+ int value_int;
+ char *value_str;
+ int len;
+ int i;
+ uint32_t index;
+ bool refresh = false;
+ bool res = false;
+
+ str = prov_file_read(filename);
+ if (!str) return false;
+
+ jmain = json_tokener_parse(str);
+ if (!jmain)
+ goto done;
+
+ if (local) {
+ json_object *jnode;
+ bool result;
+
+ json_object_object_get_ex(jmain, "node", &jnode);
+ if (!jnode) {
+ bt_shell_printf("Cannot find \"node\" object");
+ goto done;
+ } else
+ result = parse_node(jnode, true);
+
+ /*
+ * If local node is provisioner, the rest of mesh settings
+ * are read from provisioning database.
+ */
+ if (provisioner) {
+ res = result;
+ goto done;
+ }
+ }
+
+ /* IV index */
+ json_object_object_get_ex(jmain, "IVindex", &jvalue);
+ if (!jvalue)
+ goto done;
+
+ index = json_object_get_int(jvalue);
+
+ json_object_object_get_ex(jmain, "IVupdate", &jvalue);
+ if (!jvalue)
+ goto done;
+
+ value_int = json_object_get_int(jvalue);
+
+ net_set_iv_index(index, value_int);
+
+ /* Network key(s) */
+ json_object_object_get_ex(jmain, "netKeys", &jarray);
+ if (!jarray)
+ goto done;
+
+ len = json_object_array_length(jarray);
+ bt_shell_printf("# netkeys = %d\n", len);
+
+ for (i = 0; i < len; ++i) {
+ uint32_t idx;
+
+ jtemp = json_object_array_get_idx(jarray, i);
+ json_object_object_get_ex(jtemp, "index", &jvalue);
+ if (!jvalue)
+ goto done;
+ idx = json_object_get_int(jvalue);
+
+ json_object_object_get_ex(jtemp, "key", &jvalue);
+ if (!jvalue) {
+ if (!mesh_get_random_bytes(key, 16))
+ goto done;
+ add_key(jtemp, "key", key);
+ refresh = true;
+ } else {
+ value_str = (char *)json_object_get_string(jvalue);
+ if (!str2hex(value_str, strlen(value_str), key, 16)) {
+ goto done;
+ }
+ }
+
+ if (!keys_net_key_add(idx, key, false))
+ goto done;
+
+ json_object_object_get_ex(jtemp, "keyRefresh", &jvalue);
+ if (!jvalue)
+ goto done;
+
+ keys_set_kr_phase(idx, (uint8_t) json_object_get_int(jvalue));
+ }
+
+ /* App keys */
+ json_object_object_get_ex(jmain, "appKeys", &jarray);
+ if (jarray) {
+ len = json_object_array_length(jarray);
+ bt_shell_printf("# appkeys = %d\n", len);
+
+ for (i = 0; i < len; ++i) {
+ int app_idx;
+ int net_idx;
+
+ jtemp = json_object_array_get_idx(jarray, i);
+ json_object_object_get_ex(jtemp, "index",
+ &jvalue);
+ if (!jvalue)
+ goto done;
+
+ app_idx = json_object_get_int(jvalue);
+ if (!CHECK_KEY_IDX_RANGE(app_idx))
+ goto done;
+
+ json_object_object_get_ex(jtemp, "key", &jvalue);
+ if (!jvalue) {
+ if (!mesh_get_random_bytes(key, 16))
+ goto done;
+ add_key(jtemp, "key", key);
+ refresh = true;
+ } else {
+ value_str =
+ (char *)json_object_get_string(jvalue);
+ str2hex(value_str, strlen(value_str), key, 16);
+ }
+
+ json_object_object_get_ex(jtemp, "boundNetKey",
+ &jvalue);
+ if (!jvalue)
+ goto done;
+
+ net_idx = json_object_get_int(jvalue);
+ if (!CHECK_KEY_IDX_RANGE(net_idx))
+ goto done;
+
+ keys_app_key_add(net_idx, app_idx, key, false);
+ }
+ }
+
+ /* Provisioner info */
+ json_object_object_get_ex(jmain, "provisioners", &jarray);
+ if (!jarray)
+ goto done;
+
+ len = json_object_array_length(jarray);
+ bt_shell_printf("# provisioners = %d\n", len);
+
+ for (i = 0; i < len; ++i) {
+
+ jprov = json_object_array_get_idx(jarray, i);
+
+ /* Allocated unicast range */
+ json_object_object_get_ex(jprov, "allocatedUnicastRange",
+ &jtemp);
+ if (!jtemp) {
+ goto done;
+ }
+
+ if (!parse_unicast_range(jtemp)) {
+ bt_shell_printf("Doneed to parse unicast range\n");
+ goto done;
+ }
+ }
+
+ json_object_object_get_ex(jmain, "nodes", &jarray);
+ if (!jarray) {
+ res = true;
+ goto done;
+ }
+
+ len = json_object_array_length(jarray);
+
+ bt_shell_printf("# provisioned nodes = %d\n", len);
+ for (i = 0; i < len; ++i) {
+ json_object *jnode;
+ jnode = json_object_array_get_idx(jarray, i);
+
+ if (!jnode || !parse_node(jnode, false))
+ goto done;
+ }
+
+ res = true;
+done:
+
+ g_free(str);
+
+ if (res && refresh)
+ prov_file_write(jmain, false);
+
+ if (jmain)
+ json_object_put(jmain);
+
+ return res;
+}
+
+bool prov_db_read(const char *filename)
+{
+ prov_filename = filename;
+ return read_json_db(filename, true, false);
+}
+
+bool prov_db_read_local_node(const char *filename, bool provisioner)
+{
+ local_filename = filename;
+ return read_json_db(filename, provisioner, true);
+}
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2017 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
+ *
+ */
+
+bool prov_db_show(const char *filename);
+bool prov_db_read(const char *filename);
+bool prov_db_read_local_node(const char *filename, bool provisioner);
+bool prov_db_add_new_node(struct mesh_node *node);
+bool prov_db_add_node_composition(struct mesh_node *node, uint8_t *data,
+ uint16_t len);
+bool prov_db_node_keys(struct mesh_node *node, GList *idxs, const char *desc);
+bool prov_db_add_binding(struct mesh_node *node, uint8_t ele_idx,
+ uint32_t model_id, uint16_t app_idx);
+bool prov_db_node_set_ttl(struct mesh_node *node, uint8_t ttl);
+bool prov_db_node_set_iv_seq(struct mesh_node *node, uint32_t iv, uint32_t seq);
+bool prov_db_local_set_iv_index(uint32_t iv_index, bool update, bool prov);
+bool prov_db_local_set_seq_num(uint32_t seq_num);
+bool prov_db_node_set_model_pub(struct mesh_node *node, uint8_t ele_idx,
+ uint32_t model_id,
+ struct mesh_publication *pub);
+void prov_db_print_node_composition(struct mesh_node *node);
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2017 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>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <sys/uio.h>
+#include <wordexp.h>
+
+#include <readline/readline.h>
+#include <readline/history.h>
+#include <glib.h>
+
+#include "src/shared/util.h"
+#include "src/shared/ecc.h"
+#include "src/shared/shell.h"
+
+#include "gdbus/gdbus.h"
+#include "mesh/node.h"
+#include "mesh/gatt.h"
+#include "mesh/crypto.h"
+#include "mesh/mesh-net.h"
+#include "mesh/util.h"
+#include "mesh/agent.h"
+#include "mesh/prov.h"
+#include "mesh/net.h"
+
+/* Provisioning Security Levels */
+#define MESH_PROV_SEC_HIGH 2
+#define MESH_PROV_SEC_MED 1
+#define MESH_PROV_SEC_LOW 0
+
+
+#define PROV_INVITE 0x00
+#define PROV_CAPS 0x01
+#define PROV_START 0x02
+#define PROV_PUB_KEY 0x03
+#define PROV_INP_CMPLT 0x04
+#define PROV_CONFIRM 0x05
+#define PROV_RANDOM 0x06
+#define PROV_DATA 0x07
+#define PROV_COMPLETE 0x08
+#define PROV_FAILED 0x09
+
+#define PROV_NO_OOB 0
+#define PROV_STATIC_OOB 1
+#define PROV_OUTPUT_OOB 2
+#define PROV_INPUT_OOB 3
+
+#define PROV_ERR_INVALID_PDU 0x01
+#define PROV_ERR_INVALID_FORMAT 0x02
+#define PROV_ERR_UNEXPECTED_PDU 0x03
+#define PROV_ERR_CONFIRM_FAILED 0x04
+#define PROV_ERR_INSUF_RESOURCE 0x05
+#define PROV_ERR_DECRYPT_FAILED 0x06
+#define PROV_ERR_UNEXPECTED_ERR 0x07
+#define PROV_ERR_CANT_ASSIGN_ADDR 0x08
+
+/* For Deployment, Security levels below HIGH are *not* recomended */
+static uint8_t prov_sec_level = MESH_PROV_SEC_MED;
+
+/* Expected Provisioning PDU sizes */
+static const uint16_t expected_pdu_size[] = {
+ 1 + 1, /* PROV_INVITE */
+ 1 + 1 + 2 + 1 + 1 + 1 + 2 + 1 + 2, /* PROV_CAPS */
+ 1 + 1 + 1 + 1 + 1 + 1, /* PROV_START */
+ 1 + 64, /* PROV_PUB_KEY */
+ 1, /* PROV_INP_CMPLT */
+ 1 + 16, /* PROV_CONFIRM */
+ 1 + 16, /* PROV_RANDOM */
+ 1 + 16 + 2 + 1 + 4 + 2 + 8, /* PROV_DATA */
+ 1, /* PROV_COMPLETE */
+ 1 + 1, /* PROV_FAILED */
+};
+
+typedef struct __packed {
+ uint8_t attention;
+} __attribute__ ((packed)) prov_invite;
+
+typedef struct {
+ uint8_t num_ele;
+ uint16_t algorithms;
+ uint8_t pub_type;
+ uint8_t static_type;
+ uint8_t output_size;
+ uint16_t output_action;
+ uint8_t input_size;
+ uint16_t input_action;
+} __attribute__ ((packed)) prov_caps;
+
+typedef struct {
+ uint8_t algorithm;
+ uint8_t pub_key;
+ uint8_t auth_method;
+ uint8_t auth_action;
+ uint8_t auth_size;
+} __attribute__ ((packed)) prov_start;
+
+typedef struct {
+ prov_invite invite;
+ prov_caps caps;
+ prov_start start;
+ uint8_t prv_pub_key[64];
+ uint8_t dev_pub_key[64];
+} __attribute__ ((packed)) conf_input;
+
+struct prov_data {
+ GDBusProxy *prov_in;
+ provision_done_cb prov_done;
+ void *user_data;
+ uint16_t net_idx;
+ uint16_t new_addr;
+ uint8_t state;
+ uint8_t eph_priv_key[32];
+ uint8_t ecdh_secret[32];
+ conf_input conf_in;
+ uint8_t rand_auth[32];
+ uint8_t salt[16];
+ uint8_t conf_key[16];
+ uint8_t mesh_conf[16];
+ uint8_t dev_key[16];
+};
+
+static uint8_t u16_highest_bit(uint16_t mask)
+{
+ uint8_t cnt = 0;
+
+ if (!mask) return 0xff;
+
+ while (mask & 0xfffe) {
+ cnt++;
+ mask >>= 1;
+ }
+
+ return cnt;
+}
+
+bool prov_open(struct mesh_node *node, GDBusProxy *prov_in, uint16_t net_idx,
+ provision_done_cb cb, void *user_data)
+{
+ uint8_t invite[] = { PROXY_PROVISIONING_PDU, PROV_INVITE, 0x10 };
+ struct prov_data *prov = node_get_prov(node);
+
+ if (prov) return false;
+
+ prov = g_new0(struct prov_data, 1);
+ prov->prov_in = prov_in;
+ prov->net_idx = net_idx;
+ prov->prov_done = cb;
+ prov->user_data = user_data;
+ node_set_prov(node, prov);
+ prov->conf_in.invite.attention = invite[2];
+ prov->state = PROV_INVITE;
+
+ bt_shell_printf("Open-Node: %p\n", node);
+ bt_shell_printf("Open-Prov: %p\n", prov);
+ bt_shell_printf("Open-Prov: proxy %p\n", prov_in);
+
+ return mesh_gatt_write(prov_in, invite, sizeof(invite), NULL, node);
+}
+
+static bool prov_send_prov_data(void *node)
+{
+ struct prov_data *prov = node_get_prov(node);
+ uint8_t out[35] = { PROXY_PROVISIONING_PDU, PROV_DATA };
+ uint8_t key[16];
+ uint8_t nonce[13];
+ uint64_t mic;
+
+ if (prov == NULL) return false;
+
+ mesh_crypto_session_key(prov->ecdh_secret, prov->salt, key);
+ mesh_crypto_nonce(prov->ecdh_secret, prov->salt, nonce);
+ mesh_crypto_device_key(prov->ecdh_secret, prov->salt, prov->dev_key);
+
+ print_byte_array("S-Key\t", key, sizeof(key));
+ print_byte_array("S-Nonce\t", nonce, sizeof(nonce));
+ print_byte_array("DevKey\t", prov->dev_key, sizeof(prov->dev_key));
+
+ if (!net_get_key(prov->net_idx, out + 2))
+ return false;
+
+ put_be16(prov->net_idx, out + 2 + 16);
+ net_get_flags(prov->net_idx, out + 2 + 16 + 2);
+ put_be32(net_get_iv_index(NULL), out + 2 + 16 + 2 + 1);
+ put_be16(prov->new_addr, out + 2 + 16 + 2 + 1 + 4);
+
+ print_byte_array("Data\t", out + 2, 16 + 2 + 1 + 4 + 2);
+
+ mesh_crypto_aes_ccm_encrypt(nonce, key,
+ NULL, 0,
+ out + 2,
+ sizeof(out) - 2 - sizeof(mic),
+ out + 2,
+ &mic, sizeof(mic));
+
+ print_byte_array("DataEncrypted + mic\t", out + 2, sizeof(out) - 2);
+
+ prov->state = PROV_DATA;
+ return mesh_gatt_write(prov->prov_in, out, sizeof(out), NULL, node);
+}
+
+static bool prov_send_confirm(void *node)
+{
+ struct prov_data *prov = node_get_prov(node);
+ uint8_t out[18] = { PROXY_PROVISIONING_PDU, PROV_CONFIRM };
+
+ if (prov == NULL) return false;
+
+ mesh_get_random_bytes(prov->rand_auth, 16);
+
+ mesh_crypto_aes_cmac(prov->conf_key, prov->rand_auth,
+ sizeof(prov->rand_auth), out + 2);
+
+ prov->state = PROV_CONFIRM;
+ return mesh_gatt_write(prov->prov_in, out, sizeof(out), NULL, node);
+}
+
+static void prov_out_oob_done(oob_type_t type, void *buf, uint16_t len,
+ void *node)
+{
+ struct prov_data *prov = node_get_prov(node);
+
+ if (prov == NULL) return;
+
+ switch (type) {
+ default:
+ case NONE:
+ case OUTPUT:
+ prov_complete(node, PROV_ERR_INVALID_PDU);
+ return;
+
+ case ASCII:
+ case HEXADECIMAL:
+ if (len > 16)
+ prov_complete(node, PROV_ERR_INVALID_PDU);
+
+ memcpy(prov->rand_auth + 16, buf, len);
+ break;
+
+ case DECIMAL:
+ if (len != 4)
+ prov_complete(node, PROV_ERR_INVALID_PDU);
+
+ memcpy(prov->rand_auth +
+ sizeof(prov->rand_auth) -
+ sizeof(uint32_t),
+ buf, len);
+ break;
+ }
+
+ prov_send_confirm(node);
+}
+
+static uint32_t power_ten(uint8_t power)
+{
+ uint32_t ret = 1;
+
+ while (power--)
+ ret *= 10;
+
+ return ret;
+}
+
+char *in_action[3] = {
+ "Push",
+ "Twist",
+ "Enter"
+};
+
+static void prov_calc_ecdh(DBusMessage *message, void *node)
+{
+ struct prov_data *prov = node_get_prov(node);
+ uint8_t action = prov->conf_in.start.auth_action;
+ uint8_t size = prov->conf_in.start.auth_size;
+ char in_oob_display[100];
+ uint8_t *tmp = (void *) in_oob_display;
+ uint32_t in_oob;
+
+ if (prov == NULL) return;
+
+ /* Convert to Mesh byte order */
+ memcpy(tmp, prov->conf_in.dev_pub_key, 64);
+ swap_u256_bytes(tmp);
+ swap_u256_bytes(tmp + 32);
+
+ ecdh_shared_secret(tmp, prov->eph_priv_key, prov->ecdh_secret);
+
+ /* Convert to Mesh byte order */
+ swap_u256_bytes(prov->ecdh_secret);
+
+ mesh_crypto_s1(&prov->conf_in,
+ sizeof(prov->conf_in), prov->salt);
+
+ mesh_crypto_prov_conf_key(prov->ecdh_secret,
+ prov->salt, prov->conf_key);
+
+ switch (prov->conf_in.start.auth_method) {
+ default:
+ prov_complete(node, PROV_ERR_INVALID_PDU);
+ break;
+
+ case 0: /* No OOB */
+ prov_send_confirm(node);
+ break;
+
+ case 1: /* Static OOB */
+ agent_input_request(HEXADECIMAL,
+ 16,
+ prov_out_oob_done, node);
+ break;
+
+ case 2: /* Output OOB */
+ if (action <= 3)
+ agent_input_request(DECIMAL,
+ size,
+ prov_out_oob_done, node);
+ else
+ agent_input_request(ASCII,
+ size,
+ prov_out_oob_done, node);
+ break;
+
+ case 3: /* Input OOB */
+
+ if (action <= 2) {
+ mesh_get_random_bytes(&in_oob, sizeof(in_oob));
+ in_oob %= power_ten(size);
+ sprintf(in_oob_display, "%s %d on device\n",
+ in_action[action], in_oob);
+ put_be32(in_oob,
+ prov->rand_auth +
+ sizeof(prov->rand_auth) -
+ sizeof(uint32_t));
+ } else {
+ uint8_t in_ascii[9];
+ int i = size;
+
+ mesh_get_random_bytes(in_ascii, i);
+
+ while (i--) {
+ in_ascii[i] =
+ in_ascii[i] % ((26 * 2) + 10);
+ if (in_ascii[i] >= 10 + 26)
+ in_ascii[i] += 'a' - (10 + 26);
+ else if (in_ascii[i] >= 10)
+ in_ascii[i] += 'A' - 10;
+ else
+ in_ascii[i] += '0';
+ }
+ in_ascii[size] = '\0';
+ memcpy(prov->rand_auth + 16, in_ascii, size);
+ sprintf(in_oob_display,
+ "Enter %s on device\n",
+ in_ascii);
+ }
+ bt_shell_printf("Agent String: %s\n", in_oob_display);
+ agent_output_request(in_oob_display);
+ break;
+ }
+}
+
+static void prov_send_pub_key(struct mesh_node *node)
+{
+ struct prov_data *prov = node_get_prov(node);
+ uint8_t out[66] = { PROXY_PROVISIONING_PDU, PROV_PUB_KEY };
+ GDBusReturnFunction cb = NULL;
+
+ if (prov == NULL) return;
+
+ if (prov->conf_in.start.pub_key)
+ cb = prov_calc_ecdh;
+
+ memcpy(out + 2, prov->conf_in.prv_pub_key, 64);
+ prov->state = PROV_PUB_KEY;
+ mesh_gatt_write(prov->prov_in, out, 66, cb, node);
+}
+
+static void prov_oob_pub_key(oob_type_t type, void *buf, uint16_t len,
+ void *node)
+{
+ struct prov_data *prov = node_get_prov(node);
+
+ if (prov == NULL) return;
+
+ memcpy(prov->conf_in.dev_pub_key, buf, 64);
+ prov_send_pub_key(node);
+}
+
+static void prov_start_cmplt(DBusMessage *message, void *node)
+{
+ struct prov_data *prov = node_get_prov(node);
+
+ if (prov == NULL) return;
+
+ if (prov->conf_in.start.pub_key)
+ agent_input_request(HEXADECIMAL, 64, prov_oob_pub_key, node);
+ else
+ prov_send_pub_key(node);
+}
+
+bool prov_data_ready(struct mesh_node *node, uint8_t *buf, uint8_t len)
+{
+ struct prov_data *prov = node_get_prov(node);
+ uint8_t sec_level = MESH_PROV_SEC_HIGH;
+ uint8_t out[35] = { PROXY_PROVISIONING_PDU };
+
+ if (prov == NULL || len < 2) return false;
+
+ buf++;
+ len--;
+
+ bt_shell_printf("Got provisioning data (%d bytes)\n", len);
+
+ if (buf[0] > PROV_FAILED || expected_pdu_size[buf[0]] != len)
+ return prov_complete(node, PROV_ERR_INVALID_PDU);
+
+ print_byte_array("\t", buf, len);
+
+ if (buf[0] == PROV_FAILED)
+ return prov_complete(node, buf[1]);
+
+ /* Check provisioning state */
+ switch (prov->state) {
+ default:
+ return prov_complete(node, PROV_ERR_INVALID_PDU);
+
+ case PROV_INVITE:
+
+ if (buf[0] != PROV_CAPS)
+ return prov_complete(node,
+ PROV_ERR_INVALID_PDU);
+
+ /* Normalize to beginning of packed Param struct */
+ buf++;
+ len--;
+
+ /* Save Capability values */
+ memcpy(&prov->conf_in.caps, buf, len);
+
+ sec_level = prov_get_sec_level();
+
+ if (sec_level == MESH_PROV_SEC_HIGH) {
+
+ /* Enforce High Security */
+ if (prov->conf_in.caps.pub_type != 1 &&
+ prov->conf_in.caps.static_type != 1)
+ return prov_complete(node,
+ PROV_ERR_INVALID_PDU);
+
+ } else if (sec_level == MESH_PROV_SEC_MED) {
+
+ /* Enforce Medium Security */
+ if (prov->conf_in.caps.pub_type != 1 &&
+ prov->conf_in.caps.static_type != 1 &&
+ prov->conf_in.caps.input_size == 0 &&
+ prov->conf_in.caps.output_size == 0)
+ return prov_complete(node,
+ PROV_ERR_INVALID_PDU);
+
+ }
+
+ /* Num Elements cannot be Zero */
+ if (prov->conf_in.caps.num_ele == 0)
+ return prov_complete(node,
+ PROV_ERR_INVALID_PDU);
+
+ /* All nodes must support Algorithm 0x0001 */
+ if (!(get_be16(buf + 1) & 0x0001))
+ return prov_complete(node,
+ PROV_ERR_INVALID_PDU);
+
+ /* Pub Key and Static type may not be > 1 */
+ if (prov->conf_in.caps.pub_type > 0x01 ||
+ prov->conf_in.caps.static_type > 0x01)
+ return prov_complete(node,
+ PROV_ERR_INVALID_PDU);
+
+ prov->new_addr =
+ net_obtain_address(prov->conf_in.caps.num_ele);
+
+ if (!prov->new_addr)
+ return prov_complete(node,
+ PROV_ERR_INVALID_PDU);
+
+ out[1] = PROV_START;
+ prov->conf_in.start.algorithm = 0;
+ prov->conf_in.start.pub_key =
+ prov->conf_in.caps.pub_type;
+
+ /* Compose START based on most secure values */
+ if (prov->conf_in.caps.static_type) {
+
+ prov->conf_in.start.auth_method =
+ PROV_STATIC_OOB;
+
+ } else if (prov->conf_in.caps.output_size >
+ prov->conf_in.caps.input_size) {
+
+ prov->conf_in.start.auth_method =
+ PROV_OUTPUT_OOB;
+ prov->conf_in.start.auth_action =
+ u16_highest_bit(get_be16(buf + 6));
+ prov->conf_in.start.auth_size =
+ prov->conf_in.caps.output_size;
+
+ } else if (prov->conf_in.caps.input_size > 0) {
+
+ prov->conf_in.start.auth_method =
+ PROV_INPUT_OOB;
+ prov->conf_in.start.auth_action =
+ u16_highest_bit(get_be16(buf + 9));
+ prov->conf_in.start.auth_size =
+ prov->conf_in.caps.input_size;
+ }
+
+ /* Range Check START values */
+ if (prov->conf_in.start.auth_size > 8)
+ return prov_complete(node,
+ PROV_ERR_INVALID_PDU);
+
+ prov->state = PROV_START;
+
+ memcpy(out + 2, &prov->conf_in.start, 5);
+
+ ecc_make_key(prov->conf_in.prv_pub_key,
+ prov->eph_priv_key);
+
+ /* Swap public key to share into Mesh byte ordering */
+ swap_u256_bytes(prov->conf_in.prv_pub_key);
+ swap_u256_bytes(prov->conf_in.prv_pub_key + 32);
+
+ return mesh_gatt_write(prov->prov_in, out, 7,
+ prov_start_cmplt, node);
+
+
+ case PROV_PUB_KEY:
+ if (buf[0] == PROV_PUB_KEY &&
+ !prov->conf_in.start.pub_key) {
+
+ memcpy(prov->conf_in.dev_pub_key, buf + 1, 64);
+ prov_calc_ecdh(NULL, node);
+ return true;
+
+ } else if (buf[0] == PROV_INP_CMPLT) {
+ agent_output_request_cancel();
+ return prov_send_confirm(node);
+ } else
+ return prov_complete(node,
+ PROV_ERR_INVALID_PDU);
+
+ case PROV_CONFIRM:
+ if (buf[0] != PROV_CONFIRM)
+ return prov_complete(node,
+ PROV_ERR_INVALID_PDU);
+
+ memcpy(prov->mesh_conf, buf + 1, 16);
+
+ out[1] = PROV_RANDOM;
+ memcpy(out + 2, prov->rand_auth, 16);
+
+ prov->state = PROV_RANDOM;
+ return mesh_gatt_write(prov->prov_in, out, 18,
+ NULL, node);
+
+ case PROV_RANDOM:
+ if (buf[0] != PROV_RANDOM)
+ return prov_complete(node,
+ PROV_ERR_INVALID_PDU);
+
+ /* Calculate New Salt while we still have
+ * both random values */
+ mesh_crypto_prov_prov_salt(prov->salt,
+ prov->rand_auth,
+ buf + 1,
+ prov->salt);
+
+ /* Calculate meshs Conf Value */
+ memcpy(prov->rand_auth, buf + 1, 16);
+ mesh_crypto_aes_cmac(prov->conf_key, prov->rand_auth,
+ sizeof(prov->rand_auth), out + 1);
+
+ /* Validate Mesh confirmation */
+ if (memcmp(out + 1, prov->mesh_conf, 16) != 0)
+ return prov_complete(node,
+ PROV_ERR_INVALID_PDU);
+
+ bt_shell_printf("Confirmation Validated\n");
+
+ prov_send_prov_data(node);
+
+ return true;
+
+ case PROV_DATA:
+ if (buf[0] != PROV_COMPLETE)
+ return prov_complete(node,
+ PROV_ERR_INVALID_PDU);
+
+ return prov_complete(node, 0);
+ }
+
+
+
+ /* Compose appropriate reply for the prov state message */
+ /* Send reply via mesh_gatt_write() */
+ /* If done, call prov_done calllback and free prov housekeeping data */
+ bt_shell_printf("Got provisioning data (%d bytes)\n", len);
+ print_byte_array("\t", buf, len);
+
+ return true;
+}
+
+bool prov_complete(struct mesh_node *node, uint8_t status)
+{
+ struct prov_data *prov = node_get_prov(node);
+ void *user_data;
+ provision_done_cb cb;
+
+ if (prov == NULL) return false;
+
+ if (status && prov->new_addr && prov->conf_in.caps.num_ele) {
+ net_release_address(prov->new_addr, prov->conf_in.caps.num_ele);
+ }
+
+ if (!status) {
+ node_set_num_elements(node, prov->conf_in.caps.num_ele);
+ node_set_primary(node, prov->new_addr);
+ node_set_device_key(node, prov->dev_key);
+ node_net_key_add(node, prov->net_idx);
+ }
+
+ user_data = prov->user_data;
+ cb = prov->prov_done;
+ g_free(prov);
+ node_set_prov(node, NULL);
+ if (cb) cb(user_data, status);
+
+ return true;
+}
+
+bool prov_set_sec_level(uint8_t level)
+{
+ if (level > MESH_PROV_SEC_HIGH)
+ return false;
+
+ prov_sec_level = level;
+
+ return true;
+}
+
+uint8_t prov_get_sec_level(void)
+{
+ return prov_sec_level;
+}
*
* BlueZ - Bluetooth protocol stack for Linux
*
- * Copyright (C) 2014 Intel Corporation. All rights reserved.
+ * Copyright (C) 2017 Intel Corporation. All rights reserved.
*
*
- * This library is free software; you can rebastribute it and/or
+ * 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 bastributed in the hope that it will be useful,
+ * 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.
*
*/
-struct bt_bas;
+struct prov;
-struct bt_bas *bt_bas_new(void *primary);
+typedef void (*provision_done_cb)(void *user_data, int status);
-struct bt_bas *bt_bas_ref(struct bt_bas *bas);
-void bt_bas_unref(struct bt_bas *bas);
-
-bool bt_bas_attach(struct bt_bas *bas, void *gatt);
-void bt_bas_detach(struct bt_bas *bas);
+bool prov_open(struct mesh_node *node, GDBusProxy *prov_in, uint16_t net_idx,
+ provision_done_cb cb, void *user_data);
+bool prov_data_ready(struct mesh_node *node, uint8_t *buf, uint8_t len);
+bool prov_complete(struct mesh_node *node, uint8_t status);
+bool prov_set_sec_level(uint8_t level);
+uint8_t prov_get_sec_level(void);
--- /dev/null
+{
+ "$schema":"file:\/\/\/BlueZ\/Mesh\/schema\/mesh.jsonschema",
+ "meshName":"BT Mesh",
+ "IVindex":5,
+ "IVupdate":0,
+ "netKeys":[
+ {
+ "index":0,
+ "keyRefresh":0,
+ "key":"18eed9c2a56add85049ffc3c59ad0e12"
+ }
+ ],
+ "appKeys":[
+ {
+ "index":0,
+ "boundNetKey":0,
+ "key":"4f68ad85d9f48ac8589df665b6b49b8a"
+ },
+ {
+ "index":1,
+ "boundNetKey":0,
+ "key":"2aa2a6ded5a0798ceab5787ca3ae39fc"
+ }
+ ],
+ "provisioners":[
+ {
+ "provisionerName":"BT Mesh Provisioner",
+ "unicastAddress":"0077",
+ "allocatedUnicastRange":[
+ {
+ "lowAddress":"0100",
+ "highAddress":"7fff"
+ }
+ ]
+ }
+ ],
+}
--- /dev/null
+!_TAG_FILE_FORMAT 2 /extended format; --format=1 will not append ;" to lines/
+!_TAG_FILE_SORTED 1 /0=unsorted, 1=sorted, 2=foldcase/
+!_TAG_PROGRAM_AUTHOR Darren Hiebert /dhiebert@users.sourceforge.net/
+!_TAG_PROGRAM_NAME Exuberant Ctags //
+!_TAG_PROGRAM_URL http://ctags.sourceforge.net /official site/
+!_TAG_PROGRAM_VERSION 5.9~svn20110310 //
+ACTION_ADD node.h 26;" d
+ACTION_DELETE node.h 28;" d
+ACTION_UPDATE node.h 27;" d
+AKF_BIT net.c 144;" d file:
+AKF_HDR_SHIFT mesh-net.h 87;" d
+ALG_SET_AEAD_AUTHSIZE crypto.c 42;" d file:
+ALL_NODES_ADDRESS mesh-net.h 51;" d
+APP_IDX_ANY mesh-net.h 37;" d
+APP_IDX_DEV mesh-net.h 36;" d
+APP_IDX_INVALID mesh-net.h 39;" d
+APP_IDX_MASK mesh-net.h 35;" d
+APP_IDX_NET mesh-net.h 38;" d
+ASCII agent.h /^ ASCII,$/;" e enum:__anon2
+BLACKLIST_FILTER net.c 217;" d file:
+CHECK_KEY_IDX_RANGE prov-db.c 53;" d file:
+CMAC_MSG_MAX crypto.c 161;" d file:
+COLORED_CHG main.c 64;" d file:
+COLORED_DEL main.c 65;" d file:
+COLORED_NEW main.c 63;" d file:
+CONFIG_CLIENT_MODEL_ID config-model.h 25;" d
+CONFIG_SERVER_MODEL_ID config-model.h 24;" d
+CONN_TYPE_IDENTITY main.c 103;" d file:
+CONN_TYPE_INVALID main.c 105;" d file:
+CONN_TYPE_NETWORK main.c 102;" d file:
+CONN_TYPE_PROVISION main.c 104;" d file:
+CREDFLAG_MASK mesh-net.h 34;" d
+CTL mesh-net.h 30;" d
+DECIMAL agent.h /^ DECIMAL,$/;" e enum:__anon2
+DECIMAL_OOB_LEN agent.h 25;" d
+DEFAULT_TTL mesh-net.h 57;" d
+DISTANCE_VAL_INVALID main.c 1487;" d file:
+FILTER_ADD net.c 211;" d file:
+FILTER_DEL net.c 212;" d file:
+FILTER_SETUP net.c 210;" d file:
+FILTER_STATUS net.c 213;" d file:
+FRIENDS_ADDRESS mesh-net.h 49;" d
+FRND_CACHE_MAX mesh-net.h 45;" d
+GATT_MTU gatt.c 95;" d file:
+GATT_SAR_COMPLETE gatt.h 30;" d
+GATT_SAR_CONTINUE gatt.h 32;" d
+GATT_SAR_FIRST gatt.h 31;" d
+GATT_SAR_LAST gatt.h 33;" d
+GATT_SAR_MASK gatt.h 29;" d
+GATT_TYPE_INVALID gatt.h 34;" d
+GATT_TYPE_MASK gatt.h 35;" d
+GENERIC_ONOFF_CLIENT_MODEL_ID onoff-model.h 25;" d
+GENERIC_ONOFF_SERVER_MODEL_ID onoff-model.h 24;" d
+GROUP_ADDRESS_HIGH mesh-net.h 55;" d
+GROUP_ADDRESS_LOW mesh-net.h 54;" d
+HAS_APP_KEY mesh-net.h 89;" d
+HAS_MIC64 mesh-net.h 100;" d
+HEXADECIMAL agent.h /^ HEXADECIMAL,$/;" e enum:__anon2
+IS_ALL_NODES mesh-net.h 76;" d
+IS_GROUP mesh-net.h 74;" d
+IS_RELAYED mesh-net.h 99;" d
+IS_SEGMENTED mesh-net.h 81;" d
+IS_UNASSIGNED mesh-net.h 69;" d
+IS_UNICAST mesh-net.h 70;" d
+IS_VIRTUAL mesh-net.h 72;" d
+IV_IDX_DIFF_RANGE net.c 225;" d file:
+IV_UPD_INIT net.c 220;" d file:
+IV_UPD_NORMAL net.c 221;" d file:
+IV_UPD_NORMAL_HOLD net.c 223;" d file:
+IV_UPD_UPDATING net.c 222;" d file:
+KEY_AID_MASK mesh-net.h 84;" d
+KEY_AID_SHIFT mesh-net.h 86;" d
+KEY_CACHE_SIZE mesh-net.h 44;" d
+KEY_HDR_SHIFT mesh-net.h 88;" d
+KEY_ID_AKF mesh-net.h 85;" d
+KEY_ID_MASK mesh-net.h 83;" d
+KR_PHASE_INVALID keys.h 27;" d
+KR_PHASE_NONE keys.h 24;" d
+KR_PHASE_ONE keys.h 25;" d
+KR_PHASE_TWO keys.h 26;" d
+MAX_ASCII_OOB_LEN agent.h 26;" d
+MAX_GATT_SIZE gatt.h 27;" d
+MAX_HEXADECIMAL_OOB_LEN agent.h 24;" d
+MAX_SEG_LEN mesh-net.h 62;" d
+MAX_SEG_TO_LEN mesh-net.h 66;" d
+MAX_UNSEG_LEN mesh-net.h 61;" d
+MESH_FEATURE_FRIEND mesh-net.h 138;" d
+MESH_FEATURE_LPN mesh-net.h 139;" d
+MESH_FEATURE_PROXY mesh-net.h 137;" d
+MESH_FEATURE_RELAY mesh-net.h 136;" d
+MESH_MAX_ACCESS_PAYLOAD mesh-net.h 141;" d
+MESH_PROV_DATA_IN_UUID_STR main.c 70;" d file:
+MESH_PROV_DATA_OUT_UUID_STR gatt.c 49;" d file:
+MESH_PROV_DATA_OUT_UUID_STR main.c 71;" d file:
+MESH_PROV_SEC_HIGH prov.c 54;" d file:
+MESH_PROV_SEC_LOW prov.c 56;" d file:
+MESH_PROV_SEC_MED prov.c 55;" d file:
+MESH_PROXY_DATA_IN_UUID_STR main.c 72;" d file:
+MESH_PROXY_DATA_OUT_UUID_STR gatt.c 50;" d file:
+MESH_PROXY_DATA_OUT_UUID_STR main.c 73;" d file:
+MESH_STATUS_CANNOT_BIND mesh-net.h 156;" d
+MESH_STATUS_CANNOT_REMOVE mesh-net.h 155;" d
+MESH_STATUS_CANNOT_SET mesh-net.h 158;" d
+MESH_STATUS_CANNOT_UPDATE mesh-net.h 154;" d
+MESH_STATUS_FEAT_NOT_SUP mesh-net.h 153;" d
+MESH_STATUS_IDX_ALREADY_STORED mesh-net.h 149;" d
+MESH_STATUS_INSUFF_RESOURCES mesh-net.h 148;" d
+MESH_STATUS_INVALID_ADDRESS mesh-net.h 144;" d
+MESH_STATUS_INVALID_APPKEY mesh-net.h 146;" d
+MESH_STATUS_INVALID_BINDING mesh-net.h 160;" d
+MESH_STATUS_INVALID_MODEL mesh-net.h 145;" d
+MESH_STATUS_INVALID_NETKEY mesh-net.h 147;" d
+MESH_STATUS_INVALID_PUB_PARAM mesh-net.h 150;" d
+MESH_STATUS_NOT_SUB_MOD mesh-net.h 151;" d
+MESH_STATUS_STORAGE_FAIL mesh-net.h 152;" d
+MESH_STATUS_SUCCESS mesh-net.h 143;" d
+MESH_STATUS_UNABLE_CHANGE_STATE mesh-net.h 157;" d
+MESH_STATUS_UNSPECIFIED_ERROR mesh-net.h 159;" d
+MIN_COMPOSITION_LEN config-client.c 50;" d file:
+NET_IDX_INVALID main.c 107;" d file:
+NET_IDX_INVALID mesh-net.h 41;" d
+NET_IDX_PRIMARY mesh-net.h 42;" d
+NET_KEY_REFRESH_PHASE_NONE mesh-net.h 131;" d
+NET_KEY_REFRESH_PHASE_ONE mesh-net.h 132;" d
+NET_KEY_REFRESH_PHASE_THREE mesh-net.h 134;" d
+NET_KEY_REFRESH_PHASE_TWO mesh-net.h 133;" d
+NET_OP_FRND_CLEAR mesh-net.h 122;" d
+NET_OP_FRND_CLEAR_CONFIRM mesh-net.h 123;" d
+NET_OP_FRND_OFFER mesh-net.h 121;" d
+NET_OP_FRND_POLL mesh-net.h 118;" d
+NET_OP_FRND_REQUEST mesh-net.h 120;" d
+NET_OP_FRND_UPDATE mesh-net.h 119;" d
+NET_OP_HEARTBEAT mesh-net.h 128;" d
+NET_OP_PROXY_SUB_ADD mesh-net.h 125;" d
+NET_OP_PROXY_SUB_CONFIRM mesh-net.h 127;" d
+NET_OP_PROXY_SUB_REMOVE mesh-net.h 126;" d
+NET_OP_SEG_ACKNOWLEDGE mesh-net.h 117;" d
+NONE agent.h /^ NONE,$/;" e enum:__anon2
+OPCODE_HDR_SHIFT mesh-net.h 92;" d
+OPCODE_MASK mesh-net.h 91;" d
+OP_APPKEY_ADD config-model.h 27;" d
+OP_APPKEY_DELETE config-model.h 28;" d
+OP_APPKEY_GET config-model.h 29;" d
+OP_APPKEY_LIST config-model.h 30;" d
+OP_APPKEY_STATUS config-model.h 31;" d
+OP_APPKEY_UPDATE config-model.h 32;" d
+OP_CONFIG_BEACON_GET config-model.h 35;" d
+OP_CONFIG_BEACON_SET config-model.h 36;" d
+OP_CONFIG_BEACON_STATUS config-model.h 37;" d
+OP_CONFIG_DEFAULT_TTL_GET config-model.h 38;" d
+OP_CONFIG_DEFAULT_TTL_SET config-model.h 39;" d
+OP_CONFIG_DEFAULT_TTL_STATUS config-model.h 40;" d
+OP_CONFIG_FRIEND_GET config-model.h 41;" d
+OP_CONFIG_FRIEND_SET config-model.h 42;" d
+OP_CONFIG_FRIEND_STATUS config-model.h 43;" d
+OP_CONFIG_HEARTBEAT_PUB_GET config-model.h 74;" d
+OP_CONFIG_HEARTBEAT_PUB_SET config-model.h 75;" d
+OP_CONFIG_HEARTBEAT_PUB_STATUS config-model.h 76;" d
+OP_CONFIG_HEARTBEAT_SUB_GET config-model.h 77;" d
+OP_CONFIG_HEARTBEAT_SUB_SET config-model.h 78;" d
+OP_CONFIG_HEARTBEAT_SUB_STATUS config-model.h 79;" d
+OP_CONFIG_KEY_REFRESH_PHASE_GET config-model.h 47;" d
+OP_CONFIG_KEY_REFRESH_PHASE_SET config-model.h 48;" d
+OP_CONFIG_KEY_REFRESH_PHASE_STATUS config-model.h 49;" d
+OP_CONFIG_MODEL_PUB_GET config-model.h 50;" d
+OP_CONFIG_MODEL_PUB_SET config-model.h 51;" d
+OP_CONFIG_MODEL_PUB_STATUS config-model.h 52;" d
+OP_CONFIG_MODEL_PUB_VIRT_SET config-model.h 53;" d
+OP_CONFIG_MODEL_SUB_ADD config-model.h 54;" d
+OP_CONFIG_MODEL_SUB_DELETE config-model.h 55;" d
+OP_CONFIG_MODEL_SUB_DELETE_ALL config-model.h 56;" d
+OP_CONFIG_MODEL_SUB_GET config-model.h 68;" d
+OP_CONFIG_MODEL_SUB_LIST config-model.h 69;" d
+OP_CONFIG_MODEL_SUB_OVERWRITE config-model.h 57;" d
+OP_CONFIG_MODEL_SUB_STATUS config-model.h 58;" d
+OP_CONFIG_MODEL_SUB_VIRT_ADD config-model.h 59;" d
+OP_CONFIG_MODEL_SUB_VIRT_DELETE config-model.h 60;" d
+OP_CONFIG_MODEL_SUB_VIRT_OVERWRITE config-model.h 61;" d
+OP_CONFIG_NETWORK_TRANSMIT_GET config-model.h 62;" d
+OP_CONFIG_NETWORK_TRANSMIT_SET config-model.h 63;" d
+OP_CONFIG_NETWORK_TRANSMIT_STATUS config-model.h 64;" d
+OP_CONFIG_POLL_TIMEOUT_LIST config-model.h 72;" d
+OP_CONFIG_POLL_TIMEOUT_STATUS config-model.h 73;" d
+OP_CONFIG_PROXY_GET config-model.h 44;" d
+OP_CONFIG_PROXY_SET config-model.h 45;" d
+OP_CONFIG_PROXY_STATUS config-model.h 46;" d
+OP_CONFIG_RELAY_GET config-model.h 65;" d
+OP_CONFIG_RELAY_SET config-model.h 66;" d
+OP_CONFIG_RELAY_STATUS config-model.h 67;" d
+OP_CONFIG_VEND_MODEL_SUB_GET config-model.h 70;" d
+OP_CONFIG_VEND_MODEL_SUB_LIST config-model.h 71;" d
+OP_DEV_COMP_GET config-model.h 33;" d
+OP_DEV_COMP_STATUS config-model.h 34;" d
+OP_GENERIC_ONOFF_GET onoff-model.h 27;" d
+OP_GENERIC_ONOFF_SET onoff-model.h 28;" d
+OP_GENERIC_ONOFF_SET_UNACK onoff-model.h 29;" d
+OP_GENERIC_ONOFF_STATUS onoff-model.h 30;" d
+OP_MODEL_APP_BIND config-model.h 80;" d
+OP_MODEL_APP_GET config-model.h 94;" d
+OP_MODEL_APP_LIST config-model.h 95;" d
+OP_MODEL_APP_STATUS config-model.h 81;" d
+OP_MODEL_APP_UNBIND config-model.h 82;" d
+OP_NETKEY_ADD config-model.h 83;" d
+OP_NETKEY_DELETE config-model.h 84;" d
+OP_NETKEY_GET config-model.h 85;" d
+OP_NETKEY_LIST config-model.h 86;" d
+OP_NETKEY_STATUS config-model.h 87;" d
+OP_NETKEY_UPDATE config-model.h 88;" d
+OP_NODE_IDENTITY_GET config-model.h 89;" d
+OP_NODE_IDENTITY_SET config-model.h 90;" d
+OP_NODE_IDENTITY_STATUS config-model.h 91;" d
+OP_NODE_RESET config-model.h 92;" d
+OP_NODE_RESET_STATUS config-model.h 93;" d
+OP_UNRELIABLE util.h 28;" d
+OP_VEND_MODEL_APP_GET config-model.h 96;" d
+OP_VEND_MODEL_APP_LIST config-model.h 97;" d
+OUTPUT agent.h /^ OUTPUT,$/;" e enum:__anon2
+PKT_ACK net.c 185;" d file:
+PKT_AKF_AID net.c 169;" d file:
+PKT_CTL net.c 151;" d file:
+PKT_DST net.c 161;" d file:
+PKT_IVI net.c 146;" d file:
+PKT_NID net.c 149;" d file:
+PKT_OBO net.c 173;" d file:
+PKT_OPCODE net.c 171;" d file:
+PKT_SEGMENTED net.c 166;" d file:
+PKT_SEQ net.c 156;" d file:
+PKT_SEQ0 net.c 177;" d file:
+PKT_SRC net.c 159;" d file:
+PKT_SZMIC net.c 174;" d file:
+PKT_TRANS net.c 163;" d file:
+PKT_TRANS_LEN net.c 164;" d file:
+PKT_TTL net.c 154;" d file:
+PRIMARY_ELEMENT_IDX mesh-net.h 59;" d
+PROMPT_OFF main.c 68;" d file:
+PROMPT_ON main.c 67;" d file:
+PROV_CAPS prov.c 60;" d file:
+PROV_COMPLETE prov.c 67;" d file:
+PROV_CONFIRM prov.c 64;" d file:
+PROV_DATA prov.c 66;" d file:
+PROV_ERR_CANT_ASSIGN_ADDR prov.c 82;" d file:
+PROV_ERR_CONFIRM_FAILED prov.c 78;" d file:
+PROV_ERR_DECRYPT_FAILED prov.c 80;" d file:
+PROV_ERR_INSUF_RESOURCE prov.c 79;" d file:
+PROV_ERR_INVALID_FORMAT prov.c 76;" d file:
+PROV_ERR_INVALID_PDU prov.c 75;" d file:
+PROV_ERR_UNEXPECTED_ERR prov.c 81;" d file:
+PROV_ERR_UNEXPECTED_PDU prov.c 77;" d file:
+PROV_FAILED prov.c 68;" d file:
+PROV_INPUT_OOB prov.c 73;" d file:
+PROV_INP_CMPLT prov.c 63;" d file:
+PROV_INVITE prov.c 59;" d file:
+PROV_NO_OOB prov.c 70;" d file:
+PROV_OUTPUT_OOB prov.c 72;" d file:
+PROV_PUB_KEY prov.c 62;" d file:
+PROV_RANDOM prov.c 65;" d file:
+PROV_START prov.c 61;" d file:
+PROV_STATIC_OOB prov.c 71;" d file:
+PROXIES_ADDRESS mesh-net.h 48;" d
+PROXY_CONFIG_PDU mesh-net.h 27;" d
+PROXY_FILTER_BLACKLIST mesh-net.h 114;" d
+PROXY_FILTER_WHITELIST mesh-net.h 113;" d
+PROXY_MESH_BEACON mesh-net.h 26;" d
+PROXY_NETWORK_PDU mesh-net.h 25;" d
+PROXY_OP_FILTER_ADD mesh-net.h 108;" d
+PROXY_OP_FILTER_DEL mesh-net.h 109;" d
+PROXY_OP_FILTER_STATUS mesh-net.h 110;" d
+PROXY_OP_SET_FILTER_TYPE mesh-net.h 107;" d
+PROXY_PROVISIONING_PDU mesh-net.h 28;" d
+RELAY mesh-net.h 93;" d
+RELAYS_ADDRESS mesh-net.h 50;" d
+RELAY_HDR_SHIFT mesh-net.h 94;" d
+SEGMENTED mesh-net.h 78;" d
+SEGN_HDR_SHIFT mesh-net.h 104;" d
+SEGO_HDR_SHIFT mesh-net.h 103;" d
+SEG_HDR_SHIFT mesh-net.h 80;" d
+SEG_MASK mesh-net.h 102;" d
+SEG_MAX mesh-net.h 63;" d
+SEG_OFF mesh-net.h 65;" d
+SEG_TOTAL mesh-net.h 105;" d
+SEQ_MASK mesh-net.h 32;" d
+SEQ_ZERO_HDR_SHIFT mesh-net.h 98;" d
+SEQ_ZERO_MASK mesh-net.h 97;" d
+SET_PKT_ACK net.c 186;" d file:
+SET_PKT_AKF_AID net.c 170;" d file:
+SET_PKT_CTL net.c 152;" d file:
+SET_PKT_DST net.c 162;" d file:
+SET_PKT_IVI net.c 147;" d file:
+SET_PKT_NID net.c 150;" d file:
+SET_PKT_OPCODE net.c 172;" d file:
+SET_PKT_SEGMENTED net.c 167;" d file:
+SET_PKT_SEGN net.c 184;" d file:
+SET_PKT_SEGO net.c 181;" d file:
+SET_PKT_SEQ net.c 157;" d file:
+SET_PKT_SEQ0 net.c 178;" d file:
+SET_PKT_SRC net.c 160;" d file:
+SET_PKT_SZMIC net.c 175;" d file:
+SET_PKT_TTL net.c 155;" d file:
+SET_TRANS_ACK net.c 202;" d file:
+SET_TRANS_AKF_AID net.c 195;" d file:
+SET_TRANS_OPCODE net.c 193;" d file:
+SET_TRANS_SEGMENTD net.c 190;" d file:
+SET_TRANS_SEQ0 net.c 199;" d file:
+SOL_ALG crypto.c 38;" d file:
+SZMIC mesh-net.h 95;" d
+SZMIC_HDR_SHIFT mesh-net.h 96;" d
+TRANS_AKF net.c 196;" d file:
+TRANS_AKF_AID net.c 194;" d file:
+TRANS_LEN net.c 207;" d file:
+TRANS_OPCODE net.c 192;" d file:
+TRANS_PAYLOAD net.c 206;" d file:
+TRANS_SEGMENTED net.c 189;" d file:
+TRANS_SEGN net.c 204;" d file:
+TRANS_SEGO net.c 203;" d file:
+TRANS_SEQ0 net.c 198;" d file:
+TRANS_SZMIC net.c 197;" d file:
+TTL_MASK mesh-net.h 31;" d
+UNASSIGNED_ADDRESS mesh-net.h 47;" d
+UNSEGMENTED mesh-net.h 79;" d
+VIRTUAL_ADDRESS_HIGH mesh-net.h 53;" d
+VIRTUAL_ADDRESS_LOW mesh-net.h 52;" d
+WHITELIST_FILTER net.c 216;" d file:
+__packed prov.c /^typedef struct __packed {$/;" s file:
+aad net.c /^ uint8_t *aad;$/;" m struct:decrypt_params file:
+access_pkt_decrypt net.c /^static uint16_t access_pkt_decrypt(uint8_t *nonce, uint8_t *aad,$/;" f file:
+access_rxed net.c /^static bool access_rxed(uint8_t *nonce, uint16_t net_idx,$/;" f file:
+ack net.c /^ uint32_t ack;$/;" m struct:mesh_sar_msg file:
+ack_rxed net.c /^static bool ack_rxed(bool to, uint16_t src, uint16_t dst, bool obo,$/;" f file:
+ack_to net.c /^ guint ack_to;$/;" m struct:mesh_sar_msg file:
+acquire_notify_reply gatt.c /^static void acquire_notify_reply(DBusMessage *message, void *user_data)$/;" f file:
+acquire_setup gatt.c /^static void acquire_setup(DBusMessageIter *iter, void *user_data)$/;" f file:
+acquire_write_reply gatt.c /^static void acquire_write_reply(DBusMessage *message, void *user_data)$/;" f file:
+activity_cnt net.c /^ uint8_t activity_cnt;$/;" m struct:mesh_sar_msg file:
+adapter main.c /^struct adapter {$/;" s file:
+adapter_added main.c /^static void adapter_added(GDBusProxy *proxy)$/;" f file:
+adapter_removed main.c /^static void adapter_removed(GDBusProxy *proxy)$/;" f file:
+add_key prov-db.c /^static void add_key(json_object *jobject, const char *desc, uint8_t* key)$/;" f file:
+add_node_idxs prov-db.c /^static void add_node_idxs(json_object *jnode, const char *desc,$/;" f file:
+addr16 node.h /^ uint16_t addr16;$/;" m union:mesh_publication::__anon6
+address_pool net.c /^ GList *address_pool;$/;" m struct:mesh_net file:
+address_range net.c /^struct address_range$/;" s file:
+aes_cmac crypto.c /^static bool aes_cmac(int fd, const uint8_t *msg,$/;" f file:
+aes_cmac_N_start crypto.c /^static int aes_cmac_N_start(const uint8_t N[16])$/;" f file:
+aes_cmac_destroy crypto.c /^static void aes_cmac_destroy(int fd)$/;" f file:
+aes_cmac_one crypto.c /^static bool aes_cmac_one(const uint8_t key[16], const void *msg,$/;" f file:
+aes_cmac_setup crypto.c /^static int aes_cmac_setup(const uint8_t key[16])$/;" f file:
+aes_ecb crypto.c /^static bool aes_ecb(int fd, const uint8_t plaintext[16], uint8_t encrypted[16])$/;" f file:
+aes_ecb_destroy crypto.c /^static void aes_ecb_destroy(int fd)$/;" f file:
+aes_ecb_one crypto.c /^static bool aes_ecb_one(const uint8_t key[16],$/;" f file:
+aes_ecb_setup crypto.c /^static int aes_ecb_setup(const uint8_t key[16])$/;" f file:
+agent_completion agent.c /^bool agent_completion(void)$/;" f
+agent_input_cb agent.h /^typedef void (*agent_input_cb)(oob_type_t type, void *input, uint16_t len,$/;" t
+agent_input_request agent.c /^bool agent_input_request(oob_type_t type, uint16_t max_len, agent_input_cb cb,$/;" f
+agent_output_request agent.c /^bool agent_output_request(const char* str)$/;" f
+agent_output_request_cancel agent.c /^void agent_output_request_cancel(void)$/;" f
+akf_aid net.c /^ uint8_t akf_aid;$/;" m struct:decrypt_params file:
+akf_aid net.c /^ uint8_t akf_aid;$/;" m struct:mesh_sar_msg file:
+akf_aid net.c /^ uint8_t akf_aid;$/;" m struct:app_key_parts file:
+alg_encrypt crypto.c /^static bool alg_encrypt(int fd, const void *inbuf, size_t inlen,$/;" f file:
+alg_new crypto.c /^static int alg_new(int fd, const void *keyval, socklen_t keylen,$/;" f file:
+algorithm prov.c /^ uint8_t algorithm;$/;" m struct:__anon4 file:
+algorithms prov.c /^ uint16_t algorithms;$/;" m struct:__anon3 file:
+app_idx net.c /^ uint16_t app_idx;$/;" m struct:decrypt_params file:
+app_idx node.h /^ uint16_t app_idx;$/;" m struct:mesh_publication
+app_key_parts net.c /^struct app_key_parts {$/;" s file:
+app_keys net.c /^static GList *app_keys = NULL;$/;" v file:
+app_keys node.c /^ GList *app_keys;$/;" m struct:mesh_node file:
+append_array_variant main.c /^static void append_array_variant(DBusMessageIter *iter, int type, void *val,$/;" f file:
+append_variant main.c /^static void append_variant(DBusMessageIter *iter, int type, void *val)$/;" f file:
+attention prov.c /^ uint8_t attention;$/;" m struct:__packed file:
+auth_action prov.c /^ uint8_t auth_action;$/;" m struct:__anon4 file:
+auth_method prov.c /^ uint8_t auth_method;$/;" m struct:__anon4 file:
+auth_size prov.c /^ uint8_t auth_size;$/;" m struct:__anon4 file:
+beacon_key net.c /^ uint8_t beacon_key[16];$/;" m struct:net_key_parts file:
+beacon_update net.c /^static void beacon_update(bool first, bool iv_update, uint32_t iv_index)$/;" f file:
+bind node.h /^ node_model_bind_callback bind;$/;" m struct:mesh_model_ops
+bindings node.c /^ GList *bindings;$/;" m struct:mesh_model file:
+blacklist net.c /^ bool blacklist;$/;" m struct:mesh_net file:
+build_whitelist net.c /^struct build_whitelist {$/;" s file:
+calc_seqAuth net.c /^static uint32_t calc_seqAuth(uint32_t seq_num, uint8_t *trans)$/;" f file:
+caps prov.c /^ prov_caps caps;$/;" m struct:__anon5 file:
+cb agent.c /^ agent_input_cb cb;$/;" m struct:input_request file:
+cb gatt.c /^ GDBusReturnFunction cb;$/;" m struct:notify_data file:
+cb gatt.c /^ GDBusReturnFunction cb;$/;" m struct:write_data file:
+cb main.c /^ GDBusReturnFunction cb;$/;" m struct:disconnect_data file:
+cbs node.c /^ struct mesh_model_ops cbs;$/;" m struct:mesh_model typeref:struct:mesh_model::mesh_model_ops file:
+cfg_menu config-client.c /^static const struct bt_shell_menu cfg_menu = {$/;" v typeref:struct:bt_shell_menu file:
+char_is_mesh main.c /^static bool char_is_mesh(GDBusProxy *proxy, const char *target_uuid)$/;" f file:
+char_list main.c /^GList *char_list;$/;" v
+check_default_ctrl main.c /^static gboolean check_default_ctrl(void)$/;" f file:
+cid node.h /^ uint16_t cid;$/;" m struct:mesh_node_composition
+client_bind onoff-model.c /^static int client_bind(uint16_t app_idx, int action)$/;" f file:
+client_cbs config-client.c /^static struct mesh_model_ops client_cbs = {$/;" v typeref:struct:mesh_model_ops file:
+client_cbs onoff-model.c /^static struct mesh_model_ops client_cbs = {$/;" v typeref:struct:mesh_model_ops file:
+client_msg_recvd config-client.c /^static bool client_msg_recvd(uint16_t src, uint8_t *data,$/;" f file:
+client_msg_recvd onoff-model.c /^static bool client_msg_recvd(uint16_t src, uint8_t *data,$/;" f file:
+client_ready main.c /^static void client_ready(GDBusClient *client, void *user_data)$/;" f file:
+cmd_add_app_key config-client.c /^static void cmd_add_app_key(int argc, char *argv[])$/;" f file:
+cmd_add_net_key config-client.c /^static void cmd_add_net_key(int argc, char *argv[])$/;" f file:
+cmd_app_key config-client.c /^static void cmd_app_key(int argc, char *argv[], uint32_t opcode)$/;" f file:
+cmd_bind config-client.c /^static void cmd_bind(int argc, char *argv[])$/;" f file:
+cmd_connect main.c /^static void cmd_connect(int argc, char *argv[])$/;" f file:
+cmd_default config-client.c /^static void cmd_default(uint32_t opcode)$/;" f file:
+cmd_del_app_key config-client.c /^static void cmd_del_app_key(int argc, char *argv[])$/;" f file:
+cmd_del_net_key config-client.c /^static void cmd_del_net_key(int argc, char *argv[])$/;" f file:
+cmd_disconn main.c /^static void cmd_disconn(int argc, char *argv[])$/;" f file:
+cmd_get_app config-client.c /^static void cmd_get_app(int argc, char *argv[])$/;" f file:
+cmd_get_composition config-client.c /^static void cmd_get_composition(int argc, char *argv[])$/;" f file:
+cmd_get_ident config-client.c /^static void cmd_get_ident(int argc, char *argv[])$/;" f file:
+cmd_get_proxy config-client.c /^static void cmd_get_proxy(int argc, char *argv[])$/;" f file:
+cmd_get_pub config-client.c /^static void cmd_get_pub(int argc, char *argv[])$/;" f file:
+cmd_get_relay config-client.c /^static void cmd_get_relay(int argc, char *argv[])$/;" f file:
+cmd_get_status onoff-model.c /^static void cmd_get_status(int argc, char *argv[])$/;" f file:
+cmd_get_ttl config-client.c /^static void cmd_get_ttl(int argc, char *argv[])$/;" f file:
+cmd_info main.c /^static void cmd_info(int argc, char *argv[])$/;" f file:
+cmd_list main.c /^static void cmd_list(int argc, char *argv[])$/;" f file:
+cmd_net_key config-client.c /^static void cmd_net_key(int argc, char *argv[], uint32_t opcode)$/;" f file:
+cmd_power main.c /^static void cmd_power(int argc, char *argv[])$/;" f file:
+cmd_print_local main.c /^ static void cmd_print_local(int argc, char *argv[])$/;" f file:
+cmd_print_mesh main.c /^static void cmd_print_mesh(int argc, char *argv[])$/;" f file:
+cmd_scan_unprovisioned main.c /^static void cmd_scan_unprovisioned(int argc, char *argv[])$/;" f file:
+cmd_security main.c /^static void cmd_security(int argc, char *argv[])$/;" f file:
+cmd_select main.c /^static void cmd_select(int argc, char *argv[])$/;" f file:
+cmd_set onoff-model.c /^static void cmd_set(int argc, char *argv[])$/;" f file:
+cmd_set_hb config-client.c /^static void cmd_set_hb(int argc, char *argv[])$/;" f file:
+cmd_set_ident config-client.c /^static void cmd_set_ident(int argc, char *argv[])$/;" f file:
+cmd_set_node config-client.c /^static void cmd_set_node(int argc, char *argv[])$/;" f file:
+cmd_set_node onoff-model.c /^static void cmd_set_node(int argc, char *argv[])$/;" f file:
+cmd_set_proxy config-client.c /^static void cmd_set_proxy(int argc, char *argv[])$/;" f file:
+cmd_set_pub config-client.c /^static void cmd_set_pub(int argc, char *argv[])$/;" f file:
+cmd_set_relay config-client.c /^static void cmd_set_relay(int argc, char *argv[])$/;" f file:
+cmd_set_ttl config-client.c /^static void cmd_set_ttl(int argc, char *argv[])$/;" f file:
+cmd_show main.c /^static void cmd_show(int argc, char *argv[])$/;" f file:
+cmd_start_prov main.c /^static void cmd_start_prov(int argc, char *argv[])$/;" f file:
+cmd_sub_add config-client.c /^static void cmd_sub_add(int argc, char *argv[])$/;" f file:
+cmd_sub_get config-client.c /^static void cmd_sub_get(int argc, char *argv[])$/;" f file:
+cnt net.c /^ uint16_t cnt;$/;" m struct:mesh_destination file:
+comp node.c /^ struct mesh_node_composition *comp;$/;" m struct:mesh_node typeref:struct:mesh_node::mesh_node_composition file:
+conf_in prov.c /^ conf_input conf_in;$/;" m struct:prov_data file:
+conf_input prov.c /^} __attribute__ ((packed)) conf_input;$/;" t typeref:struct:__anon5 file:
+conf_key prov.c /^ uint8_t conf_key[16];$/;" m struct:prov_data file:
+config_client_get_composition config-client.c /^void config_client_get_composition(uint32_t dst)$/;" f
+config_client_init config-client.c /^bool config_client_init(void)$/;" f
+config_send config-client.c /^static bool config_send(uint8_t *buf, uint16_t len)$/;" f file:
+config_server_init config-server.c /^bool config_server_init(void)$/;" f
+connect_handler main.c /^static void connect_handler(DBusConnection *connection, void *user_data)$/;" f file:
+connect_reply main.c /^static void connect_reply(DBusMessage *message, void *user_data)$/;" f file:
+connection main.c /^} connection;$/;" v typeref:struct:__anon1
+credential node.h /^ uint8_t credential;$/;" m struct:mesh_publication
+crpl node.h /^ uint16_t crpl;$/;" m struct:mesh_node_composition
+crypto_128 crypto.c /^static bool crypto_128(const uint8_t n[16], const char *s, uint8_t out128[16])$/;" f file:
+ctl net.c /^ bool ctl;$/;" m struct:mesh_sar_msg file:
+ctl_rxed net.c /^static bool ctl_rxed(uint16_t net_idx, uint32_t iv_index,$/;" f file:
+ctrl_list main.c /^static GList *ctrl_list;$/;" v file:
+current net.c /^ struct app_key_parts current;$/;" m struct:mesh_app_key typeref:struct:mesh_app_key::app_key_parts file:
+current net.c /^ struct net_key_parts current;$/;" m struct:mesh_net_key typeref:struct:mesh_net_key::net_key_parts file:
+data main.c /^ void *data;$/;" m struct:disconnect_data file:
+data net.c /^ uint8_t data[20]; \/* Open ended, min 20 *\/$/;" m struct:mesh_sar_msg file:
+data net.c /^ uint8_t data[30];$/;" m struct:mesh_pkt file:
+data net.c /^ uint8_t data[12];$/;" m struct:build_whitelist file:
+data_in main.c /^ GDBusProxy *data_in;$/;" m struct:__anon1 file:
+data_out main.c /^ GDBusProxy *data_out;$/;" m struct:__anon1 file:
+data_out_notify main.c /^static void data_out_notify(GDBusProxy *proxy, bool enable,$/;" f file:
+dbus_conn main.c /^static DBusConnection *dbus_conn;$/;" v file:
+decode_params net.c /^struct decode_params {$/;" s file:
+decrypt_params net.c /^struct decrypt_params {$/;" s file:
+default_ctrl main.c /^static struct adapter *default_ctrl;$/;" v typeref:struct:adapter file:
+default_ttl net.c /^ uint8_t default_ttl;$/;" m struct:mesh_net file:
+delete_key net.c /^static bool delete_key(GList **list, uint16_t index)$/;" f file:
+deliver_model_data node.c /^static bool deliver_model_data(struct mesh_element* element, uint16_t src,$/;" f file:
+dest net.c /^ GList *dest; \/* List of valid local destinations for Whitelist *\/$/;" m struct:mesh_net file:
+dev_key node.c /^ uint8_t dev_key[16];$/;" m struct:mesh_node file:
+dev_key prov.c /^ uint8_t dev_key[16];$/;" m struct:prov_data file:
+dev_pub_key prov.c /^ uint8_t dev_pub_key[64];$/;" m struct:__anon5 file:
+dev_uuid main.c /^ uint8_t dev_uuid[16];$/;" m struct:__anon1 file:
+dev_uuid main.c /^ uint8_t dev_uuid[16];$/;" m struct:mesh_device file:
+dev_uuid node.c /^ uint8_t dev_uuid[16];$/;" m struct:mesh_node file:
+dev_uuid node.h /^ uint8_t dev_uuid[16];$/;" m struct:prov_svc_data
+device main.c /^ GDBusProxy *device;$/;" m struct:__anon1 file:
+device_is_child main.c /^static gboolean device_is_child(GDBusProxy *device, GDBusProxy *master)$/;" f file:
+device_removed main.c /^static void device_removed(GDBusProxy *proxy)$/;" f file:
+dict_append_array main.c /^static void dict_append_array(DBusMessageIter *dict, const char *key, int type,$/;" f file:
+dict_append_basic_array main.c /^static void dict_append_basic_array(DBusMessageIter *dict, int key_type,$/;" f file:
+dict_append_entry main.c /^static void dict_append_entry(DBusMessageIter *dict, const char *key,$/;" f file:
+disc_notify_cb main.c /^static void disc_notify_cb(DBusMessage *message, void *user_data)$/;" f file:
+disconn_reply main.c /^static void disconn_reply(DBusMessage *message, void *user_data)$/;" f file:
+disconnect main.c /^static void disconnect(GDBusReturnFunction cb, void *user_data)$/;" f file:
+disconnect_data main.c /^struct disconnect_data {$/;" s file:
+disconnect_device main.c /^static void disconnect_device(GDBusReturnFunction cb, void *user_data)$/;" f file:
+disconnect_handler main.c /^static void disconnect_handler(DBusConnection *connection, void *user_data)$/;" f file:
+discover_mesh main.c /^static bool discover_mesh;$/;" v file:
+discovering main.c /^static bool discovering = false;$/;" v file:
+dst net.c /^ uint16_t dst;$/;" m struct:decrypt_params file:
+dst net.c /^ uint16_t dst;$/;" m struct:mesh_destination file:
+dst net.c /^ uint32_t dst;$/;" m struct:mesh_sar_msg file:
+duplicate main.c /^ dbus_bool_t duplicate;$/;" m struct:set_discovery_filter_args file:
+ecdh_secret prov.c /^ uint8_t ecdh_secret[32];$/;" m struct:prov_data file:
+element_free node.c /^static void element_free(void *data)$/;" f file:
+elements node.c /^ GList *elements;$/;" m struct:mesh_node file:
+enable gatt.c /^ bool enable;$/;" m struct:notify_data file:
+enc_key net.c /^ uint8_t enc_key[16];$/;" m struct:net_key_parts file:
+eph_priv_key prov.c /^ uint8_t eph_priv_key[32];$/;" m struct:prov_data file:
+expected_pdu_size prov.c /^static const uint16_t expected_pdu_size[] = {$/;" v file:
+features node.c /^ uint16_t features;$/;" m struct:mesh_node file:
+filtered_scan_pathloss main.c /^static gint filtered_scan_pathloss = DISTANCE_VAL_INVALID;$/;" v file:
+filtered_scan_rssi main.c /^static gint filtered_scan_rssi = DISTANCE_VAL_INVALID;$/;" v file:
+filtered_scan_transport main.c /^static char *filtered_scan_transport = "le";$/;" v file:
+filtered_scan_uuids main.c /^static char **filtered_scan_uuids;$/;" v file:
+filtered_scan_uuids_len main.c /^static size_t filtered_scan_uuids_len;$/;" v file:
+find_app_key_by_idx net.c /^static struct mesh_app_key *find_app_key_by_idx(uint16_t app_idx)$/;" f file:
+find_configured_model prov-db.c /^static json_object* find_configured_model(struct mesh_node *node, int ele_idx,$/;" f file:
+find_ctrl_by_address main.c /^static struct adapter *find_ctrl_by_address(GList *source, const char *address)$/;" f file:
+find_device_by_proxy main.c /^static struct mesh_device *find_device_by_proxy(GList *source,$/;" f file:
+find_device_by_uuid main.c /^static struct mesh_device *find_device_by_uuid(GList *source, uint8_t uuid[16])$/;" f file:
+find_net_key_by_id net.c /^static struct mesh_net_key *find_net_key_by_id(const uint8_t *net_id)$/;" f file:
+find_net_key_by_idx net.c /^static struct mesh_net_key *find_net_key_by_idx(uint16_t net_idx)$/;" f file:
+find_node_by_primary prov-db.c /^static json_object *find_node_by_primary(json_object *jmain, uint16_t primary)$/;" f file:
+find_parent main.c /^static struct adapter *find_parent(GDBusProxy *device)$/;" f file:
+find_sar_in_by_src net.c /^static struct mesh_sar_msg *find_sar_in_by_src(uint16_t src)$/;" f file:
+find_sar_out_by_dst net.c /^static struct mesh_sar_msg *find_sar_out_by_dst(uint16_t dst)$/;" f file:
+find_virt_by_dst net.c /^static struct mesh_virt_addr *find_virt_by_dst(uint32_t dst)$/;" f file:
+flush_pkt_list net.c /^static void flush_pkt_list(GList **list)$/;" f file:
+flush_sar net.c /^static void flush_sar(GList **list, struct mesh_sar_msg *sar)$/;" f file:
+flush_sar_list net.c /^static void flush_sar_list(GList **list)$/;" f file:
+forget_mesh_devices main.c /^static void forget_mesh_devices()$/;" f file:
+free_node_resources node.c /^static void free_node_resources(void *data)$/;" f file:
+gatt_data gatt.c /^ uint8_t *gatt_data;$/;" m struct:write_data file:
+gatt_len gatt.c /^ uint8_t gatt_len;$/;" m struct:write_data file:
+generic net.c /^ struct generic_key generic;$/;" m struct:mesh_app_key typeref:struct:mesh_app_key::generic_key file:
+generic net.c /^ struct generic_key generic;$/;" m struct:mesh_net_key typeref:struct:mesh_net_key::generic_key file:
+generic_callback main.c /^static void generic_callback(const DBusError *error, void *user_data)$/;" f file:
+generic_key net.c /^struct generic_key {$/;" s file:
+get_characteristic main.c /^static GDBusProxy *get_characteristic(GDBusProxy *device, const char *char_uuid)$/;" f file:
+get_characteristic_value main.c /^static int get_characteristic_value(DBusMessageIter *value, uint8_t *buf)$/;" f file:
+get_enc_keys net.c /^static bool get_enc_keys(uint16_t app_idx, uint16_t dst,$/;" f file:
+get_jmodel_obj prov-db.c /^static json_object *get_jmodel_obj(struct mesh_node *node, uint8_t ele_idx,$/;" f file:
+get_key net.c /^static uint8_t *get_key(GList *list, uint16_t index)$/;" f file:
+get_model node.c /^static struct mesh_model *get_model(struct mesh_node *node, uint8_t ele_idx,$/;" f file:
+get_next_seq net.c /^static uint32_t get_next_seq()$/;" f file:
+get_sequence_number net.c /^uint32_t get_sequence_number(void)$/;" f
+help main.c /^static const char *help[] = {$/;" v file:
+hex2str util.c /^size_t hex2str(uint8_t *in, size_t in_len, char *out,$/;" f
+hide main.c /^ gboolean hide;$/;" m struct:mesh_device file:
+id node.c /^ uint32_t id;$/;" m struct:mesh_model file:
+identity_calc crypto.c /^static bool identity_calc(const uint8_t net_key[16], uint16_t addr,$/;" f file:
+idx net.c /^ uint16_t idx;$/;" m struct:generic_key file:
+in_action prov.c /^char *in_action[3] = {$/;" v
+index node.c /^ uint8_t index;$/;" m struct:mesh_element file:
+input_action prov.c /^ uint16_t input_action;$/;" m struct:__anon3 file:
+input_request agent.c /^struct input_request {$/;" s file:
+input_size prov.c /^ uint8_t input_size;$/;" m struct:__anon3 file:
+invite prov.c /^ prov_invite invite;$/;" m struct:__anon5 file:
+iov gatt.c /^ struct iovec iov;$/;" m struct:write_data typeref:struct:write_data::iovec file:
+iv_index net.c /^ uint32_t iv_index;$/;" m struct:decode_params file:
+iv_index net.c /^ uint32_t iv_index;$/;" m struct:decrypt_params file:
+iv_index net.c /^ uint32_t iv_index;$/;" m struct:mesh_sar_msg file:
+iv_index net.c /^ uint32_t iv_index;$/;" m struct:mesh_net file:
+iv_index node.c /^ uint32_t iv_index;$/;" m struct:mesh_node file:
+iv_upd_state net.c /^ uint8_t iv_upd_state;$/;" m struct:mesh_net file:
+iv_update net.c /^ bool iv_update;$/;" m struct:mesh_net file:
+iv_update_timeout net.c /^ guint iv_update_timeout;$/;" m struct:mesh_net file:
+key net.c /^ uint8_t key[16];$/;" m struct:app_key_parts file:
+keys_app_key_add net.c /^bool keys_app_key_add(uint16_t net_idx, uint16_t app_idx, uint8_t *key,$/;" f
+keys_app_key_delete net.c /^bool keys_app_key_delete(uint16_t app_idx)$/;" f
+keys_app_key_get net.c /^uint8_t *keys_app_key_get(uint16_t app_idx, bool current)$/;" f
+keys_app_key_get_bound net.c /^uint16_t keys_app_key_get_bound(uint16_t app_idx)$/;" f
+keys_cleanup_all net.c /^void keys_cleanup_all(void)$/;" f
+keys_get_kr_phase net.c /^uint8_t keys_get_kr_phase(uint16_t net_idx)$/;" f
+keys_net_key_add net.c /^bool keys_net_key_add(uint16_t net_idx, uint8_t *key, bool update)$/;" f
+keys_net_key_delete net.c /^bool keys_net_key_delete(uint16_t net_idx)$/;" f
+keys_net_key_get net.c /^uint8_t *keys_net_key_get(uint16_t net_idx, bool current)$/;" f
+keys_set_kr_phase net.c /^bool keys_set_kr_phase(uint16_t index, uint8_t phase)$/;" f
+len agent.c /^ uint16_t len;$/;" m struct:input_request file:
+len net.c /^ uint16_t len;$/;" m struct:decrypt_params file:
+len net.c /^ uint16_t len;$/;" m struct:mesh_sar_msg file:
+len net.c /^ uint8_t len;$/;" m struct:mesh_pkt file:
+len net.c /^ uint8_t len;$/;" m struct:build_whitelist file:
+loc node.c /^ uint16_t loc;$/;" m struct:mesh_element file:
+local_filename prov-db.c /^static const char *local_filename;$/;" v file:
+local_node node.c /^static struct mesh_node *local_node;$/;" v typeref:struct:mesh_node file:
+lpn node.h /^ bool lpn;$/;" m struct:mesh_node_composition
+main main.c /^int main(int argc, char *argv[])$/;" f
+main_menu main.c /^static const struct bt_shell_menu main_menu = {$/;" v typeref:struct:bt_shell_menu file:
+main_menu main.c /^static const struct bt_shell_menu main_menu;$/;" v typeref:struct:bt_shell_menu file:
+match_address_range net.c /^static int match_address_range(const void *a, const void *b)$/;" f file:
+match_destination net.c /^static int match_destination(const void *a, const void *b)$/;" f file:
+match_device_uuid node.c /^static int match_device_uuid(const void *a, const void *b)$/;" f file:
+match_element_idx node.c /^static int match_element_idx(const void *a, const void *b)$/;" f file:
+match_key_index net.c /^static int match_key_index(const void *a, const void *b)$/;" f file:
+match_model_id node.c /^static int match_model_id(const void *a, const void *b)$/;" f file:
+match_net_id net.c /^static int match_net_id(const void *a, const void *net_id)$/;" f file:
+match_node_unicast node.c /^static int match_node_unicast(const void *a, const void *b)$/;" f file:
+match_sar_dst net.c /^static int match_sar_dst(const void *a, const void *b)$/;" f file:
+match_sar_src net.c /^static int match_sar_src(const void *a, const void *b)$/;" f file:
+match_virt_dst net.c /^static int match_virt_dst(const void *a, const void *b)$/;" f file:
+max net.c /^ uint16_t max;$/;" m struct:address_range file:
+mesh_app_key net.c /^struct mesh_app_key {$/;" s file:
+mesh_conf prov.c /^ uint8_t mesh_conf[16];$/;" m struct:prov_data file:
+mesh_config_dir main.c /^static const char *mesh_config_dir;$/;" v file:
+mesh_crypto_aes_ccm_decrypt crypto.c /^bool mesh_crypto_aes_ccm_decrypt(const uint8_t nonce[13], const uint8_t key[16],$/;" f
+mesh_crypto_aes_ccm_encrypt crypto.c /^bool mesh_crypto_aes_ccm_encrypt(const uint8_t nonce[13], const uint8_t key[16],$/;" f
+mesh_crypto_aes_cmac crypto.c /^bool mesh_crypto_aes_cmac(const uint8_t key[16], const uint8_t *msg,$/;" f
+mesh_crypto_application_decrypt crypto.c /^bool mesh_crypto_application_decrypt(uint8_t key_id, uint32_t seq, uint16_t src,$/;" f
+mesh_crypto_application_encrypt crypto.c /^bool mesh_crypto_application_encrypt(uint8_t key_id, uint32_t seq, uint16_t src,$/;" f
+mesh_crypto_application_nonce crypto.c /^bool mesh_crypto_application_nonce(uint32_t seq, uint16_t src,$/;" f
+mesh_crypto_beacon_cmac crypto.c /^bool mesh_crypto_beacon_cmac(const uint8_t encryption_key[16],$/;" f
+mesh_crypto_device_key crypto.c /^bool mesh_crypto_device_key(const uint8_t secret[32],$/;" f
+mesh_crypto_device_nonce crypto.c /^bool mesh_crypto_device_nonce(uint32_t seq, uint16_t src,$/;" f
+mesh_crypto_identity crypto.c /^bool mesh_crypto_identity(const uint8_t net_key[16], uint16_t addr,$/;" f
+mesh_crypto_identity_check crypto.c /^bool mesh_crypto_identity_check(const uint8_t net_key[16], uint16_t addr,$/;" f
+mesh_crypto_k1 crypto.c /^bool mesh_crypto_k1(const uint8_t ikm[16], const uint8_t salt[16],$/;" f
+mesh_crypto_k2 crypto.c /^bool mesh_crypto_k2(const uint8_t n[16], const uint8_t *p, size_t p_len,$/;" f
+mesh_crypto_k3 crypto.c /^bool mesh_crypto_k3(const uint8_t n[16], uint8_t out64[8])$/;" f
+mesh_crypto_k4 crypto.c /^bool mesh_crypto_k4(const uint8_t a[16], uint8_t out6[1])$/;" f
+mesh_crypto_network_decrypt crypto.c /^bool mesh_crypto_network_decrypt(bool ctl, uint8_t ttl,$/;" f
+mesh_crypto_network_encrypt crypto.c /^bool mesh_crypto_network_encrypt(bool ctl, uint8_t ttl,$/;" f
+mesh_crypto_network_nonce crypto.c /^bool mesh_crypto_network_nonce(bool ctl, uint8_t ttl, uint32_t seq,$/;" f
+mesh_crypto_nkbk crypto.c /^bool mesh_crypto_nkbk(const uint8_t n[16], uint8_t beacon_key[16])$/;" f
+mesh_crypto_nkik crypto.c /^bool mesh_crypto_nkik(const uint8_t n[16], uint8_t identity_key[16])$/;" f
+mesh_crypto_nonce crypto.c /^bool mesh_crypto_nonce(const uint8_t secret[32],$/;" f
+mesh_crypto_packet_decode crypto.c /^bool mesh_crypto_packet_decode(const uint8_t *packet, uint8_t packet_len,$/;" f
+mesh_crypto_packet_encode crypto.c /^bool mesh_crypto_packet_encode(uint8_t *packet, uint8_t packet_len,$/;" f
+mesh_crypto_prov_conf_key crypto.c /^bool mesh_crypto_prov_conf_key(const uint8_t secret[32],$/;" f
+mesh_crypto_prov_prov_salt crypto.c /^bool mesh_crypto_prov_prov_salt(const uint8_t conf_salt[16],$/;" f
+mesh_crypto_s1 crypto.c /^bool mesh_crypto_s1(const void *info, size_t len, uint8_t salt[16])$/;" f
+mesh_crypto_session_key crypto.c /^bool mesh_crypto_session_key(const uint8_t secret[32],$/;" f
+mesh_crypto_virtual_addr crypto.c /^bool mesh_crypto_virtual_addr(const uint8_t virtual_label[16],$/;" f
+mesh_destination net.c /^struct mesh_destination {$/;" s file:
+mesh_device main.c /^struct mesh_device {$/;" s file:
+mesh_devices main.c /^ GList *mesh_devices;$/;" m struct:adapter file:
+mesh_element node.c /^struct mesh_element {$/;" s file:
+mesh_gatt_is_child gatt.c /^bool mesh_gatt_is_child(GDBusProxy *proxy, GDBusProxy *parent,$/;" f
+mesh_gatt_notify gatt.c /^bool mesh_gatt_notify(GDBusProxy *proxy, bool enable, GDBusReturnFunction cb,$/;" f
+mesh_gatt_sar gatt.c /^uint16_t mesh_gatt_sar(uint8_t **pkt, uint16_t size)$/;" f
+mesh_gatt_write gatt.c /^bool mesh_gatt_write(GDBusProxy *proxy, uint8_t *buf, uint16_t len,$/;" f
+mesh_get_random_bytes crypto.c /^bool mesh_get_random_bytes(void *buf, size_t num_bytes)$/;" f
+mesh_local_config_filename main.c /^static char *mesh_local_config_filename;$/;" v file:
+mesh_model node.c /^struct mesh_model {$/;" s file:
+mesh_model_ops node.h /^struct mesh_model_ops {$/;" s
+mesh_net net.c /^struct mesh_net {$/;" s file:
+mesh_net_key net.c /^struct mesh_net_key {$/;" s file:
+mesh_node node.c /^struct mesh_node {$/;" s file:
+mesh_node_composition node.h /^struct mesh_node_composition {$/;" s
+mesh_opcode_get util.c /^bool mesh_opcode_get(const uint8_t *buf, uint16_t sz, uint32_t *opcode, int *n)$/;" f
+mesh_opcode_set util.c /^uint16_t mesh_opcode_set(uint32_t opcode, uint8_t *buf)$/;" f
+mesh_pkt net.c /^struct mesh_pkt {$/;" s file:
+mesh_prov_db_filename main.c /^static char *mesh_prov_db_filename;$/;" v file:
+mesh_prov_done main.c /^static void mesh_prov_done(void *user_data, int status)$/;" f file:
+mesh_publication node.h /^struct mesh_publication {$/;" s
+mesh_sar_msg net.c /^struct mesh_sar_msg {$/;" s file:
+mesh_session_setup main.c /^static void mesh_session_setup(GDBusProxy *proxy)$/;" f file:
+mesh_status_str util.c /^const char *mesh_status_str(uint8_t status)$/;" f
+mesh_virt_addr net.c /^struct mesh_virt_addr {$/;" s file:
+message_handler main.c /^static void message_handler(DBusConnection *connection,$/;" f file:
+min net.c /^ uint16_t min;$/;" m struct:address_range file:
+model_free node.c /^static void model_free(void *data)$/;" f file:
+models node.c /^ GList *models;$/;" m struct:mesh_element file:
+msg_out net.c /^ GList *msg_out; \/* Pre-Network encoded, might be multi-segment *\/$/;" m struct:mesh_net file:
+msg_rxed net.c /^static bool msg_rxed(uint16_t net_idx, uint32_t iv_index, bool szmic,$/;" f file:
+msg_to net.c /^ guint msg_to;$/;" m struct:mesh_sar_msg file:
+name node.c /^ const char *name;$/;" m struct:mesh_node file:
+net net.c /^static struct mesh_net net;$/;" v typeref:struct:mesh_net file:
+net_access_layer_send net.c /^bool net_access_layer_send(uint8_t ttl, uint16_t src, uint32_t dst,$/;" f
+net_add_address_pool net.c /^bool net_add_address_pool(uint16_t min, uint16_t max)$/;" f
+net_ctl_msg_send net.c /^bool net_ctl_msg_send(uint8_t ttl, uint16_t src, uint16_t dst,$/;" f
+net_data_ready net.c /^bool net_data_ready(uint8_t *msg, uint8_t len)$/;" f
+net_dest_ref net.c /^void net_dest_ref(uint16_t dst)$/;" f
+net_dest_unref net.c /^void net_dest_unref(uint16_t dst)$/;" f
+net_get_default_ttl net.c /^uint8_t net_get_default_ttl()$/;" f
+net_get_flags net.c /^bool net_get_flags(uint16_t net_idx, uint8_t *out_flags)$/;" f
+net_get_iv_index net.c /^uint32_t net_get_iv_index(bool *update)$/;" f
+net_get_key net.c /^bool net_get_key(uint16_t net_idx, uint8_t *key)$/;" f
+net_get_seq_num net.c /^uint32_t net_get_seq_num()$/;" f
+net_id net.c /^ uint8_t net_id[8];$/;" m struct:net_key_parts file:
+net_idx main.c /^ uint16_t net_idx;$/;" m struct:__anon1 file:
+net_idx net.c /^ uint16_t net_idx;$/;" m struct:mesh_app_key file:
+net_idx net.c /^ uint16_t net_idx;$/;" m struct:decrypt_params file:
+net_idx net.c /^ uint16_t net_idx;$/;" m struct:mesh_sar_msg file:
+net_idx prov.c /^ uint16_t net_idx;$/;" m struct:prov_data file:
+net_key net.c /^ struct mesh_net_key *net_key;$/;" m struct:decode_params typeref:struct:decode_params::mesh_net_key file:
+net_key net.c /^ uint8_t net_key[16];$/;" m struct:net_key_parts file:
+net_key_parts net.c /^struct net_key_parts {$/;" s file:
+net_keys net.c /^static GList *net_keys = NULL;$/;" v file:
+net_keys node.c /^ GList *net_keys;$/;" m struct:mesh_node file:
+net_mesh_session_open_callback net.h /^typedef void (*net_mesh_session_open_callback)(int status);$/;" t
+net_obtain_address net.c /^uint16_t net_obtain_address(uint8_t num_eles)$/;" f
+net_packet_decode net.c /^static struct mesh_net_key *net_packet_decode(bool proxy, uint32_t iv_index,$/;" f file:
+net_register_group net.c /^bool net_register_group(uint16_t group_addr)$/;" f
+net_register_unicast net.c /^bool net_register_unicast(uint16_t unicast, uint8_t count)$/;" f
+net_register_virtual net.c /^uint32_t net_register_virtual(uint8_t buf[16])$/;" f
+net_release_address net.c /^void net_release_address(uint16_t addr, uint8_t num_elements)$/;" f
+net_reserve_address_range net.c /^bool net_reserve_address_range(uint16_t base, uint8_t num_elements)$/;" f
+net_session_close net.c /^void net_session_close(GDBusProxy *data_in)$/;" f
+net_session_open net.c /^bool net_session_open(GDBusProxy *data_in, bool provisioner,$/;" f
+net_set_default_ttl net.c /^bool net_set_default_ttl(uint8_t ttl)$/;" f
+net_set_iv_index net.c /^void net_set_iv_index(uint32_t iv_index, bool update)$/;" f
+net_set_seq_num net.c /^bool net_set_seq_num(uint32_t seq_num)$/;" f
+net_validate_proxy_beacon net.c /^uint16_t net_validate_proxy_beacon(const uint8_t *proxy_beacon)$/;" f
+new net.c /^ struct app_key_parts new;$/;" m struct:mesh_app_key typeref:struct:mesh_app_key::app_key_parts file:
+new net.c /^ struct net_key_parts new;$/;" m struct:mesh_net_key typeref:struct:mesh_net_key::net_key_parts file:
+new_addr prov.c /^ uint16_t new_addr;$/;" m struct:prov_data file:
+nid net.c /^ uint8_t nid;$/;" m struct:net_key_parts file:
+node_add_binding node.c /^bool node_add_binding(struct mesh_node *node, uint8_t ele_idx,$/;" f
+node_app_key_add node.c /^bool node_app_key_add(struct mesh_node *node, uint16_t idx)$/;" f
+node_app_key_delete node.c /^bool node_app_key_delete(struct mesh_node *node, uint16_t net_idx,$/;" f
+node_cleanup node.c /^void node_cleanup(void)$/;" f
+node_create_new node.c /^struct mesh_node *node_create_new(struct prov_svc_data *prov)$/;" f
+node_find_by_addr node.c /^struct mesh_node *node_find_by_addr(uint16_t addr)$/;" f
+node_find_by_uuid node.c /^struct mesh_node *node_find_by_uuid(uint8_t uuid[16])$/;" f
+node_free node.c /^void node_free(struct mesh_node *node)$/;" f
+node_get_app_keys node.c /^GList *node_get_app_keys(struct mesh_node *node)$/;" f
+node_get_composition node.c /^struct mesh_node_composition *node_get_composition(struct mesh_node *node)$/;" f
+node_get_default_ttl node.c /^uint8_t node_get_default_ttl(struct mesh_node *node)$/;" f
+node_get_device_key node.c /^uint8_t *node_get_device_key(struct mesh_node *node)$/;" f
+node_get_iv_index node.c /^uint32_t node_get_iv_index(struct mesh_node *node)$/;" f
+node_get_local_node node.c /^struct mesh_node *node_get_local_node()$/;" f
+node_get_net_keys node.c /^GList *node_get_net_keys(struct mesh_node *node)$/;" f
+node_get_num_elements node.c /^uint8_t node_get_num_elements(struct mesh_node *node)$/;" f
+node_get_primary node.c /^uint16_t node_get_primary(struct mesh_node *node)$/;" f
+node_get_primary_net_idx node.c /^uint16_t node_get_primary_net_idx(struct mesh_node *node)$/;" f
+node_get_prov node.c /^void *node_get_prov(struct mesh_node *node)$/;" f
+node_get_sequence_number node.c /^uint32_t node_get_sequence_number(struct mesh_node *node)$/;" f
+node_is_provisioned node.c /^bool node_is_provisioned(struct mesh_node *node)$/;" f
+node_local_data_handler node.c /^void node_local_data_handler(uint16_t src, uint32_t dst,$/;" f
+node_local_model_register node.c /^bool node_local_model_register(uint8_t ele_idx, uint16_t model_id,$/;" f
+node_local_vendor_model_register node.c /^bool node_local_vendor_model_register(uint8_t ele_idx, uint32_t model_id,$/;" f
+node_model_bind_callback node.h /^typedef int (*node_model_bind_callback)(uint16_t app_idx, int action);$/;" t
+node_model_pub_callback node.h /^typedef void (*node_model_pub_callback)(struct mesh_publication *pub);$/;" t
+node_model_pub_get node.c /^struct mesh_publication *node_model_pub_get(struct mesh_node *node, uint8_t ele,$/;" f
+node_model_pub_set node.c /^bool node_model_pub_set(struct mesh_node *node, uint8_t ele, uint32_t model_id,$/;" f
+node_model_recv_callback node.h /^typedef bool (*node_model_recv_callback)(uint16_t src, uint8_t *data,$/;" t
+node_model_sub_callback node.h /^typedef void (*node_model_sub_callback)(uint16_t sub_addr, int action);$/;" t
+node_net_key_add node.c /^bool node_net_key_add(struct mesh_node *node, uint16_t index)$/;" f
+node_net_key_delete node.c /^bool node_net_key_delete(struct mesh_node *node, uint16_t index)$/;" f
+node_new node.c /^struct mesh_node *node_new(void)$/;" f
+node_parse_composition node.c /^bool node_parse_composition(struct mesh_node *node, uint8_t *data, uint16_t len)$/;" f
+node_set_composition node.c /^bool node_set_composition(struct mesh_node *node,$/;" f
+node_set_default_ttl node.c /^bool node_set_default_ttl(struct mesh_node *node, uint8_t ttl)$/;" f
+node_set_device_key node.c /^void node_set_device_key(struct mesh_node *node, uint8_t *key)$/;" f
+node_set_element node.c /^bool node_set_element(struct mesh_node *node, uint8_t ele_idx)$/;" f
+node_set_iv_index node.c /^bool node_set_iv_index(struct mesh_node *node, uint32_t iv_index)$/;" f
+node_set_local_node node.c /^bool node_set_local_node(struct mesh_node *node)$/;" f
+node_set_model node.c /^bool node_set_model(struct mesh_node *node, uint8_t ele_idx, uint32_t id)$/;" f
+node_set_num_elements node.c /^void node_set_num_elements(struct mesh_node *node, uint8_t num_ele)$/;" f
+node_set_primary node.c /^void node_set_primary(struct mesh_node *node, uint16_t unicast)$/;" f
+node_set_prov node.c /^void node_set_prov(struct mesh_node *node, void *prov)$/;" f
+node_set_sequence_number node.c /^bool node_set_sequence_number(struct mesh_node *node, uint32_t seq)$/;" f
+nodes node.c /^static GList *nodes;$/;" v file:
+nonce net.c /^ uint8_t *nonce;$/;" m struct:decrypt_params file:
+notify_data gatt.c /^struct notify_data {$/;" s file:
+notify_io gatt.c /^static struct io *notify_io;$/;" v typeref:struct:io file:
+notify_io_destroy gatt.c /^static void notify_io_destroy(void)$/;" f file:
+notify_mtu gatt.c /^static uint16_t notify_mtu;$/;" v file:
+notify_prov_out_cb main.c /^static void notify_prov_out_cb(DBusMessage *message, void *user_data)$/;" f file:
+notify_proxy_out_cb main.c /^static void notify_proxy_out_cb(DBusMessage *message, void *user_data)$/;" f file:
+notify_reply gatt.c /^static void notify_reply(DBusMessage *message, void *user_data)$/;" f file:
+num_ele node.c /^ uint8_t num_ele;$/;" m struct:mesh_node file:
+num_ele prov.c /^ uint8_t num_ele;$/;" m struct:__anon3 file:
+num_elements net.c /^ uint8_t num_elements;$/;" m struct:mesh_net file:
+onoff_app_idx onoff-model.c /^static uint16_t onoff_app_idx = APP_IDX_INVALID;$/;" v file:
+onoff_client_init onoff-model.c /^bool onoff_client_init(uint8_t ele)$/;" f
+onoff_menu onoff-model.c /^static const struct bt_shell_menu onoff_menu = {$/;" v typeref:struct:bt_shell_menu file:
+oob node.c /^ uint16_t oob;$/;" m struct:mesh_node file:
+oob node.h /^ uint16_t oob;$/;" m struct:prov_svc_data
+oob_type_t agent.h /^} oob_type_t;$/;" t typeref:enum:__anon2
+open_cb net.c /^ net_mesh_session_open_callback open_cb;$/;" m struct:mesh_net file:
+opt main.c /^static const struct bt_shell_opt opt = {$/;" v typeref:struct:bt_shell_opt file:
+optargs main.c /^static const char **optargs[] = {$/;" v file:
+options main.c /^static const struct option options[] = {$/;" v typeref:struct:option file:
+out_msg net.c /^ uint8_t *out_msg;$/;" m struct:decrypt_params file:
+output_action prov.c /^ uint16_t output_action;$/;" m struct:__anon3 file:
+output_size prov.c /^ uint8_t output_size;$/;" m struct:__anon3 file:
+packet net.c /^ uint8_t *packet;$/;" m struct:decode_params file:
+parms config-client.c /^static uint32_t parms[8];$/;" v file:
+parms onoff-model.c /^static uint32_t parms[8];$/;" v file:
+parse_argument_on_off main.c /^static gboolean parse_argument_on_off(int argc, char *argv[],$/;" f file:
+parse_bindings prov-db.c /^static bool parse_bindings(struct mesh_node *node, int ele_idx,$/;" f file:
+parse_composition_elements prov-db.c /^static bool parse_composition_elements(struct mesh_node *node,$/;" f file:
+parse_composition_models prov-db.c /^static bool parse_composition_models(struct mesh_node *node, int index,$/;" f file:
+parse_configuration_elements prov-db.c /^static bool parse_configuration_elements(struct mesh_node *node,$/;" f file:
+parse_configuration_models prov-db.c /^static bool parse_configuration_models(struct mesh_node *node, int ele_idx,$/;" f file:
+parse_mesh_service_data main.c /^static bool parse_mesh_service_data(const char *uuid, uint8_t *data, int len,$/;" f file:
+parse_model_pub prov-db.c /^static bool parse_model_pub(struct mesh_node *node, int ele_idx,$/;" f file:
+parse_node prov-db.c /^static bool parse_node(json_object *jnode, bool local)$/;" f file:
+parse_node_composition prov-db.c /^static bool parse_node_composition(struct mesh_node *node, json_object *jcomp)$/;" f file:
+parse_node_keys prov-db.c /^static int parse_node_keys(struct mesh_node *node, json_object *jidxs,$/;" f file:
+parse_prov_service_data main.c /^static bool parse_prov_service_data(const char *uuid, uint8_t *data, int len,$/;" f file:
+parse_service_data main.c /^static bool parse_service_data(GDBusProxy *proxy, const char *target_uuid,$/;" f file:
+parse_unicast_range prov-db.c /^static bool parse_unicast_range(json_object *jobject)$/;" f file:
+pathloss main.c /^ dbus_int16_t pathloss;$/;" m struct:set_discovery_filter_args file:
+pending_request agent.c /^static struct input_request pending_request = {NONE, 0, NULL, NULL};$/;" v typeref:struct:input_request file:
+period node.h /^ uint8_t period;$/;" m struct:mesh_publication
+phase net.c /^ uint8_t phase;$/;" m struct:mesh_net_key file:
+pid node.h /^ uint16_t pid;$/;" m struct:mesh_node_composition
+pipe_hup gatt.c /^static bool pipe_hup(struct io *io, void *user_data)$/;" f file:
+pipe_io_new gatt.c /^static struct io *pipe_io_new(int fd)$/;" f file:
+pipe_read gatt.c /^static bool pipe_read(struct io *io, bool prov, void *user_data)$/;" f file:
+pipe_read_prov gatt.c /^static bool pipe_read_prov(struct io *io, void *user_data)$/;" f file:
+pipe_read_proxy gatt.c /^static bool pipe_read_proxy(struct io *io, void *user_data)$/;" f file:
+pipe_write gatt.c /^static bool pipe_write(struct io *io, void *user_data)$/;" f file:
+pkt_out net.c /^ GList *pkt_out; \/* Fully encoded packets awaiting Tx in order *\/$/;" m struct:mesh_net file:
+power_ten agent.c /^static uint32_t power_ten(uint8_t power)$/;" f file:
+power_ten prov.c /^static uint32_t power_ten(uint8_t power)$/;" f file:
+primary node.c /^ uint16_t primary;$/;" m struct:mesh_node file:
+primary_addr net.c /^ uint16_t primary_addr;$/;" m struct:mesh_net file:
+primary_net_idx node.c /^ uint16_t primary_net_idx;$/;" m struct:mesh_node file:
+print_adapter main.c /^static void print_adapter(GDBusProxy *proxy, const char *description)$/;" f file:
+print_byte_array util.c /^void print_byte_array(const char *prefix, const void *ptr, int len)$/;" f
+print_device main.c /^static void print_device(GDBusProxy *proxy, const char *description)$/;" f file:
+print_iter main.c /^static void print_iter(const char *label, const char *name,$/;" f file:
+print_model_pub util.c /^void print_model_pub(uint16_t ele_addr, uint32_t mod_id,$/;" f
+print_property main.c /^static void print_property(GDBusProxy *proxy, const char *name)$/;" f file:
+print_prov_service main.c /^static void print_prov_service(struct prov_svc_data *prov_data)$/;" f file:
+print_remaining_time onoff-model.c /^static void print_remaining_time(uint8_t remaining_time)$/;" f file:
+print_uuids main.c /^static void print_uuids(GDBusProxy *proxy)$/;" f file:
+privacy_key net.c /^ uint8_t privacy_key[16];$/;" m struct:net_key_parts file:
+process_beacon net.c /^static bool process_beacon(uint8_t *data, uint8_t size)$/;" f file:
+process_mesh_characteristic main.c /^static bool process_mesh_characteristic(GDBusProxy *proxy)$/;" f file:
+property_changed main.c /^static void property_changed(GDBusProxy *proxy, const char *name,$/;" f file:
+prov node.c /^ void *prov;$/;" m struct:mesh_node file:
+prov_calc_ecdh prov.c /^static void prov_calc_ecdh(DBusMessage *message, void *node)$/;" f file:
+prov_caps prov.c /^} __attribute__ ((packed)) prov_caps;$/;" t typeref:struct:__anon3 file:
+prov_complete prov.c /^bool prov_complete(struct mesh_node *node, uint8_t status)$/;" f
+prov_data prov.c /^struct prov_data {$/;" s file:
+prov_data_ready prov.c /^bool prov_data_ready(struct mesh_node *node, uint8_t *buf, uint8_t len)$/;" f
+prov_db_add_binding prov-db.c /^bool prov_db_add_binding(struct mesh_node *node, uint8_t ele_idx,$/;" f
+prov_db_add_new_node prov-db.c /^bool prov_db_add_new_node(struct mesh_node *node)$/;" f
+prov_db_add_node_composition prov-db.c /^bool prov_db_add_node_composition(struct mesh_node *node, uint8_t *data,$/;" f
+prov_db_local_set_iv_index prov-db.c /^bool prov_db_local_set_iv_index(uint32_t iv_index, bool update, bool prov)$/;" f
+prov_db_local_set_seq_num prov-db.c /^bool prov_db_local_set_seq_num(uint32_t seq_num)$/;" f
+prov_db_node_keys prov-db.c /^bool prov_db_node_keys(struct mesh_node *node, GList *idxs, const char *desc)$/;" f
+prov_db_node_set_iv_seq prov-db.c /^bool prov_db_node_set_iv_seq(struct mesh_node *node, uint32_t iv, uint32_t seq)$/;" f
+prov_db_node_set_model_pub prov-db.c /^bool prov_db_node_set_model_pub(struct mesh_node *node, uint8_t ele_idx,$/;" f
+prov_db_node_set_ttl prov-db.c /^bool prov_db_node_set_ttl(struct mesh_node *node, uint8_t ttl)$/;" f
+prov_db_print_node_composition prov-db.c /^void prov_db_print_node_composition(struct mesh_node *node)$/;" f
+prov_db_read prov-db.c /^bool prov_db_read(const char *filename)$/;" f
+prov_db_read_local_node prov-db.c /^bool prov_db_read_local_node(const char *filename, bool provisioner)$/;" f
+prov_db_show prov-db.c /^bool prov_db_show(const char *filename)$/;" f
+prov_disconn_reply main.c /^static void prov_disconn_reply(DBusMessage *message, void *user_data)$/;" f file:
+prov_done prov.c /^ provision_done_cb prov_done;$/;" m struct:prov_data file:
+prov_file_read prov-db.c /^static char* prov_file_read(const char *filename)$/;" f file:
+prov_file_write prov-db.c /^static void prov_file_write(json_object *jmain, bool local)$/;" f file:
+prov_filename prov-db.c /^static const char *prov_filename;$/;" v file:
+prov_get_sec_level prov.c /^uint8_t prov_get_sec_level(void)$/;" f
+prov_in prov.c /^ GDBusProxy *prov_in;$/;" m struct:prov_data file:
+prov_invite prov.c /^} __attribute__ ((packed)) prov_invite;$/;" t typeref:struct:__packed file:
+prov_net_key_index main.c /^static uint16_t prov_net_key_index = NET_IDX_PRIMARY;$/;" v file:
+prov_oob_pub_key prov.c /^static void prov_oob_pub_key(oob_type_t type, void *buf, uint16_t len,$/;" f file:
+prov_open prov.c /^bool prov_open(struct mesh_node *node, GDBusProxy *prov_in, uint16_t net_idx,$/;" f
+prov_out_oob_done prov.c /^static void prov_out_oob_done(oob_type_t type, void *buf, uint16_t len,$/;" f file:
+prov_sec_level prov.c /^static uint8_t prov_sec_level = MESH_PROV_SEC_MED;$/;" v file:
+prov_send_confirm prov.c /^static bool prov_send_confirm(void *node)$/;" f file:
+prov_send_prov_data prov.c /^static bool prov_send_prov_data(void *node)$/;" f file:
+prov_send_pub_key prov.c /^static void prov_send_pub_key(struct mesh_node *node)$/;" f file:
+prov_set_sec_level prov.c /^bool prov_set_sec_level(uint8_t level)$/;" f
+prov_start prov.c /^} __attribute__ ((packed)) prov_start;$/;" t typeref:struct:__anon4 file:
+prov_start_cmplt prov.c /^static void prov_start_cmplt(DBusMessage *message, void *node)$/;" f file:
+prov_svc_data node.h /^struct prov_svc_data {$/;" s
+provision_done_cb prov.h /^typedef void (*provision_done_cb)(void *user_data, int status);$/;" t
+provisioner net.c /^ bool provisioner;$/;" m struct:mesh_net file:
+provisioner node.c /^ bool provisioner;$/;" m struct:mesh_node file:
+proxy gatt.c /^ GDBusProxy *proxy;$/;" m struct:notify_data file:
+proxy gatt.c /^ GDBusProxy *proxy;$/;" m struct:write_data file:
+proxy main.c /^ GDBusProxy *proxy;$/;" m struct:mesh_device file:
+proxy main.c /^GDBusProxy *proxy;$/;" m struct:adapter file:
+proxy net.c /^ bool proxy;$/;" m struct:decode_params file:
+proxy net.c /^ bool proxy;$/;" m struct:mesh_sar_msg file:
+proxy node.h /^ bool proxy;$/;" m struct:mesh_node_composition
+proxy_added main.c /^static void proxy_added(GDBusProxy *proxy, void *user_data)$/;" f file:
+proxy_ctl_rxed net.c /^static bool proxy_ctl_rxed(uint16_t net_idx, uint32_t iv_index,$/;" f file:
+proxy_in net.c /^ GDBusProxy *proxy_in;$/;" m struct:mesh_net file:
+proxy_leak main.c /^static void proxy_leak(gpointer data)$/;" f file:
+proxy_removed main.c /^static void proxy_removed(GDBusProxy *proxy, void *user_data)$/;" f file:
+prv_pub_key prov.c /^ uint8_t prv_pub_key[64];$/;" m struct:__anon5 file:
+pub node.c /^ struct mesh_publication *pub;$/;" m struct:mesh_model typeref:struct:mesh_model::mesh_publication file:
+pub node.h /^ node_model_pub_callback pub;$/;" m struct:mesh_model_ops
+pub_key prov.c /^ uint8_t pub_key;$/;" m struct:__anon4 file:
+pub_type prov.c /^ uint8_t pub_type;$/;" m struct:__anon3 file:
+put_uint16 prov-db.c /^static void put_uint16(json_object *jobject, const char *desc, uint16_t value)$/;" f file:
+put_uint16_array_entry prov-db.c /^static void put_uint16_array_entry(json_object *jarray, uint16_t value)$/;" f file:
+put_uint16_list prov-db.c /^static void put_uint16_list(json_object *jarray, GList *list)$/;" f file:
+put_uint32 prov-db.c /^static void put_uint32(json_object *jobject, const char *desc, uint32_t value)$/;" f file:
+put_uint32_array_entry prov-db.c /^static void put_uint32_array_entry(json_object *jarray, uint32_t value)$/;" f file:
+rand_auth prov.c /^ uint8_t rand_auth[32];$/;" m struct:prov_data file:
+range_cmp net.c /^static int range_cmp(const void *a, const void *b)$/;" f file:
+read_input_parameters config-client.c /^static uint32_t read_input_parameters(int argc, char *argv[])$/;" f file:
+read_input_parameters onoff-model.c /^static uint32_t read_input_parameters(int argc, char *argv[])$/;" f file:
+read_json_db prov-db.c /^static bool read_json_db(const char *filename, bool provisioner, bool local)$/;" f file:
+recv node.h /^ node_model_recv_callback recv;$/;" m struct:mesh_model_ops
+relay node.h /^ bool relay;$/;" m struct:mesh_node_composition
+request_ascii agent.c /^static bool request_ascii(uint16_t len)$/;" f file:
+request_decimal agent.c /^static bool request_decimal(uint16_t len)$/;" f file:
+request_hexadecimal agent.c /^static bool request_hexadecimal(uint16_t len)$/;" f file:
+resend_segs net.c /^static void resend_segs(struct mesh_sar_msg *sar)$/;" f file:
+resend_unacked_segs net.c /^static void resend_unacked_segs(gpointer data, gpointer user_data)$/;" f file:
+reset_input_request agent.c /^static void reset_input_request(void)$/;" f file:
+response_ascii agent.c /^static void response_ascii(const char *input, void *user_data)$/;" f file:
+response_decimal agent.c /^static void response_decimal(const char *input, void *user_data)$/;" f file:
+response_hexadecimal agent.c /^static void response_hexadecimal(const char *input, void *user_data)$/;" f file:
+response_output agent.c /^static void response_output(const char *input, void *user_data)$/;" f file:
+restore_model_state node.c /^static gboolean restore_model_state(gpointer data)$/;" f file:
+retransmit node.h /^ uint8_t retransmit;$/;" m struct:mesh_publication
+rssi main.c /^ dbus_uint16_t rssi;$/;" m struct:set_discovery_filter_args file:
+salt prov.c /^ uint8_t salt[16];$/;" m struct:prov_data file:
+sar_in net.c /^ GList *sar_in; \/* Incoming segmented messages in progress *\/$/;" m struct:mesh_net file:
+sar_in_ack_timeout net.c /^static gboolean sar_in_ack_timeout(void *user_data)$/;" f file:
+sar_in_msg_timeout net.c /^static gboolean sar_in_msg_timeout(void *user_data)$/;" f file:
+sar_out_ack_timeout net.c /^static gboolean sar_out_ack_timeout(void *user_data)$/;" f file:
+sar_out_msg_timeout net.c /^static gboolean sar_out_msg_timeout(void *user_data)$/;" f file:
+security2str main.c /^static const char *security2str(uint8_t level)$/;" f file:
+segN net.c /^ uint8_t segN;$/;" m struct:mesh_sar_msg file:
+seg_rxed net.c /^static bool seg_rxed(uint16_t net_idx, uint32_t iv_index, bool ctl,$/;" f file:
+segmented net.c /^ bool segmented;$/;" m struct:mesh_sar_msg file:
+send_cmd onoff-model.c /^static bool send_cmd(uint8_t *buf, uint16_t len)$/;" f file:
+send_mesh_pkt net.c /^static void send_mesh_pkt(struct mesh_pkt *pkt)$/;" f file:
+send_pkt_cmplt net.c /^static void send_pkt_cmplt(DBusMessage *message, void *user_data)$/;" f file:
+send_sar_ack net.c /^static void send_sar_ack(struct mesh_sar_msg *sar)$/;" f file:
+send_seg net.c /^static void send_seg(struct mesh_sar_msg *sar, uint8_t seg)$/;" f file:
+seqAuth net.c /^ uint32_t seqAuth;$/;" m struct:mesh_sar_msg file:
+seq_num net.c /^ uint32_t seq_num;$/;" m struct:decrypt_params file:
+seq_num net.c /^ uint32_t seq_num;$/;" m struct:mesh_net file:
+seq_num_reserved net.c /^ uint32_t seq_num_reserved;$/;" m struct:mesh_net file:
+seq_number node.c /^ uint32_t seq_number;$/;" m struct:mesh_node file:
+server_cbs config-server.c /^static struct mesh_model_ops server_cbs = {$/;" v typeref:struct:mesh_model_ops file:
+server_msg_recvd config-server.c /^static bool server_msg_recvd(uint16_t src, uint8_t *data,$/;" f file:
+service main.c /^ GDBusProxy *service;$/;" m struct:__anon1 file:
+service_is_mesh main.c /^static bool service_is_mesh(GDBusProxy *proxy, const char *target_uuid)$/;" f file:
+service_list main.c /^GList *service_list;$/;" v
+session_open main.c /^ bool session_open;$/;" m struct:__anon1 file:
+session_open_cb main.c /^static void session_open_cb (int status)$/;" f file:
+set_connected_device main.c /^static void set_connected_device(GDBusProxy *proxy)$/;" f file:
+set_discovery_filter_args main.c /^struct set_discovery_filter_args {$/;" s file:
+set_discovery_filter_reply main.c /^static void set_discovery_filter_reply(DBusMessage *message, void *user_data)$/;" f file:
+set_discovery_filter_setup main.c /^static void set_discovery_filter_setup(DBusMessageIter *iter, void *user_data)$/;" f file:
+set_local_iv_index prov-db.c /^static void set_local_iv_index(json_object *jobj, uint32_t idx, bool update)$/;" f file:
+set_menu_prompt util.c /^void set_menu_prompt(const char *name, const char *id)$/;" f
+set_scan_filter_commit main.c /^static void set_scan_filter_commit(void)$/;" f file:
+set_scan_filter_uuids main.c /^static void set_scan_filter_uuids(char *filters[])$/;" f file:
+set_sequence_number net.c /^void set_sequence_number(uint32_t seq_num)$/;" f
+setup_whitelist net.c /^static void setup_whitelist()$/;" f file:
+size net.c /^ uint8_t size;$/;" m struct:decode_params file:
+src net.c /^ uint16_t src;$/;" m struct:decrypt_params file:
+src net.c /^ uint16_t src;$/;" m struct:mesh_sar_msg file:
+start prov.c /^ prov_start start;$/;" m struct:__anon5 file:
+start_discovery_reply main.c /^static void start_discovery_reply(DBusMessage *message, void *user_data)$/;" f file:
+state prov.c /^ uint8_t state;$/;" m struct:prov_data file:
+static_type prov.c /^ uint8_t static_type;$/;" m struct:__anon3 file:
+str2hex util.c /^bool str2hex(const char *str, uint16_t in_len, uint8_t *out,$/;" f
+sub node.h /^ node_model_sub_callback sub;$/;" m struct:mesh_model_ops
+subscriptions node.c /^ GList *subscriptions;$/;" m struct:mesh_model file:
+swap_u256_bytes util.c /^void swap_u256_bytes(uint8_t *u256)$/;" f
+szmic net.c /^ bool szmic;$/;" m struct:decrypt_params file:
+szmic net.c /^ bool szmic;$/;" m struct:mesh_sar_msg file:
+target config-client.c /^static uint32_t target;$/;" v file:
+target onoff-model.c /^static uint32_t target;$/;" v file:
+trans net.c /^ uint8_t *trans;$/;" m struct:decrypt_params file:
+trans_id onoff-model.c /^static uint8_t trans_id;$/;" v file:
+transport main.c /^ char *transport;$/;" m struct:set_discovery_filter_args file:
+try_decode net.c /^static void try_decode(gpointer data, gpointer user_data)$/;" f file:
+try_decrypt net.c /^static void try_decrypt(gpointer data, gpointer user_data)$/;" f file:
+try_virt_decrypt net.c /^static void try_virt_decrypt(gpointer data, gpointer user_data)$/;" f file:
+ttl net.c /^ uint8_t ttl;$/;" m struct:mesh_sar_msg file:
+ttl node.c /^ uint8_t ttl;$/;" m struct:mesh_node file:
+ttl node.h /^ uint8_t ttl;$/;" m struct:mesh_publication
+type agent.c /^ oob_type_t type;$/;" m struct:input_request file:
+type main.c /^ uint8_t type;$/;" m struct:__anon1 file:
+u node.h /^ } u;$/;" m struct:mesh_publication typeref:union:mesh_publication::__anon6
+u16_highest_bit prov.c /^static uint8_t u16_highest_bit(uint16_t mask)$/;" f file:
+unicast main.c /^ uint16_t unicast;$/;" m struct:__anon1 file:
+update_device_info main.c /^static void update_device_info(GDBusProxy *proxy)$/;" f file:
+user_data agent.c /^ void *user_data;$/;" m struct:input_request file:
+user_data gatt.c /^ void *user_data;$/;" m struct:notify_data file:
+user_data gatt.c /^ void *user_data;$/;" m struct:write_data file:
+user_data node.c /^ void *user_data;$/;" m struct:mesh_model file:
+user_data prov.c /^ void *user_data;$/;" m struct:prov_data file:
+uuids main.c /^ char **uuids;$/;" m struct:set_discovery_filter_args file:
+uuids_len main.c /^ size_t uuids_len;$/;" m struct:set_discovery_filter_args file:
+va128 net.c /^ uint8_t va128[16];$/;" m struct:mesh_virt_addr file:
+va16 net.c /^ uint16_t va16;$/;" m struct:mesh_virt_addr file:
+va32 net.c /^ uint32_t va32;$/;" m struct:mesh_virt_addr file:
+va_128 node.h /^ uint8_t va_128[16];$/;" m union:mesh_publication::__anon6
+verify_config_target config-client.c /^static bool verify_config_target(uint32_t dst)$/;" f file:
+vid node.h /^ uint16_t vid;$/;" m struct:mesh_node_composition
+virt_addrs net.c /^static GList *virt_addrs = NULL;$/;" v file:
+virtual_rxed net.c /^static bool virtual_rxed(uint8_t *nonce, uint16_t net_idx,$/;" f file:
+whitefilter_add net.c /^static void whitefilter_add(gpointer data, gpointer user_data)$/;" f file:
+write_data gatt.c /^struct write_data {$/;" s file:
+write_data_free gatt.c /^static void write_data_free(void *user_data)$/;" f file:
+write_io gatt.c /^static struct io *write_io;$/;" v typeref:struct:io file:
+write_io_destroy gatt.c /^static void write_io_destroy(void)$/;" f file:
+write_mtu gatt.c /^static uint16_t write_mtu;$/;" v file:
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2017 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>
+#include <stdbool.h>
+#include <inttypes.h>
+
+#include <glib.h>
+
+#include "src/shared/shell.h"
+#include "src/shared/util.h"
+#include "mesh/mesh-net.h"
+#include "mesh/node.h"
+#include "mesh/util.h"
+
+void set_menu_prompt(const char *name, const char *id)
+{
+ char *prompt;
+
+ prompt = g_strdup_printf(COLOR_BLUE "[%s%s%s]" COLOR_OFF "# ", name,
+ id ? ": Target = " : "", id ? id : "");
+ bt_shell_set_prompt(prompt);
+ g_free(prompt);
+}
+
+void print_byte_array(const char *prefix, const void *ptr, int len)
+{
+ const uint8_t *data = ptr;
+ char *line, *bytes;
+ int i;
+
+ line = g_malloc(strlen(prefix) + (16 * 3) + 2);
+ sprintf(line, "%s ", prefix);
+ bytes = line + strlen(prefix) + 1;
+
+ for (i = 0; i < len; ++i) {
+ sprintf(bytes, "%2.2x ", data[i]);
+ if ((i + 1) % 16) {
+ bytes += 3;
+ } else {
+ bt_shell_printf("\r%s\n", line);
+ bytes = line + strlen(prefix) + 1;
+ }
+ }
+
+ if (i % 16)
+ bt_shell_printf("\r%s\n", line);
+
+ g_free(line);
+}
+
+bool str2hex(const char *str, uint16_t in_len, uint8_t *out,
+ uint16_t out_len)
+{
+ uint16_t i;
+
+ if (in_len < out_len * 2)
+ return false;
+
+ for (i = 0; i < out_len; i++) {
+ if (sscanf(&str[i * 2], "%02hhx", &out[i]) != 1)
+ return false;
+ }
+
+ return true;
+}
+
+size_t hex2str(uint8_t *in, size_t in_len, char *out,
+ size_t out_len)
+{
+ static const char hexdigits[] = "0123456789abcdef";
+ size_t i;
+
+ if(in_len * 2 > out_len - 1)
+ return 0;
+
+ for (i = 0; i < in_len; i++) {
+ out[i * 2] = hexdigits[in[i] >> 4];
+ out[i * 2 + 1] = hexdigits[in[i] & 0xf];
+ }
+
+ out[in_len * 2] = '\0';
+ return i;
+}
+
+uint16_t mesh_opcode_set(uint32_t opcode, uint8_t *buf)
+{
+ if (opcode <= 0x7e) {
+ buf[0] = opcode;
+ return 1;
+ } else if (opcode >= 0x8000 && opcode <= 0xbfff) {
+ put_be16(opcode, buf);
+ return 2;
+ } else if (opcode >= 0xc00000 && opcode <= 0xffffff) {
+ buf[0] = (opcode >> 16) & 0xff;
+ put_be16(opcode, buf + 1);
+ return 3;
+ } else {
+ bt_shell_printf("Illegal Opcode %x", opcode);
+ return 0;
+ }
+}
+
+bool mesh_opcode_get(const uint8_t *buf, uint16_t sz, uint32_t *opcode, int *n)
+{
+ if (!n || !opcode || sz < 1) return false;
+
+ switch (buf[0] & 0xc0) {
+ case 0x00:
+ case 0x40:
+ /* RFU */
+ if (buf[0] == 0x7f)
+ return false;
+
+ *n = 1;
+ *opcode = buf[0];
+ break;
+
+ case 0x80:
+ if (sz < 2)
+ return false;
+
+ *n = 2;
+ *opcode = get_be16(buf);
+ break;
+
+ case 0xc0:
+ if (sz < 3)
+ return false;
+
+ *n = 3;
+ *opcode = get_be16(buf + 1);
+ *opcode |= buf[0] << 16;
+ break;
+
+ default:
+ bt_shell_printf("Bad Packet:\n");
+ print_byte_array("\t", (void *) buf, sz);
+ return false;
+ }
+
+ return true;
+}
+
+const char *mesh_status_str(uint8_t status)
+{
+ switch (status) {
+ case MESH_STATUS_SUCCESS: return "Success";
+ case MESH_STATUS_INVALID_ADDRESS: return "Invalid Address";
+ case MESH_STATUS_INVALID_MODEL: return "Invalid Model";
+ case MESH_STATUS_INVALID_APPKEY: return "Invalid AppKey";
+ case MESH_STATUS_INVALID_NETKEY: return "Invalid NetKey";
+ case MESH_STATUS_INSUFF_RESOURCES: return "Insufficient Resources";
+ case MESH_STATUS_IDX_ALREADY_STORED: return "Key Idx Already Stored";
+ case MESH_STATUS_INVALID_PUB_PARAM: return "Invalid Publish Parameters";
+ case MESH_STATUS_NOT_SUB_MOD: return "Not a Subscribe Model";
+ case MESH_STATUS_STORAGE_FAIL: return "Storage Failure";
+ case MESH_STATUS_FEAT_NOT_SUP: return "Feature Not Supported";
+ case MESH_STATUS_CANNOT_UPDATE: return "Cannot Update";
+ case MESH_STATUS_CANNOT_REMOVE: return "Cannot Remove";
+ case MESH_STATUS_CANNOT_BIND: return "Cannot bind";
+ case MESH_STATUS_UNABLE_CHANGE_STATE: return "Unable to change state";
+ case MESH_STATUS_CANNOT_SET: return "Cannot set";
+ case MESH_STATUS_UNSPECIFIED_ERROR: return "Unspecified error";
+ case MESH_STATUS_INVALID_BINDING: return "Invalid Binding";
+
+ default: return "Unknown";
+ }
+}
+
+void print_model_pub(uint16_t ele_addr, uint32_t mod_id,
+ struct mesh_publication *pub)
+{
+ bt_shell_printf("\tElement: %4.4x\n", ele_addr);
+ bt_shell_printf("\tPub Addr: %4.4x", pub->u.addr16);
+ if (mod_id > 0xffff0000)
+ bt_shell_printf("\tModel: %8.8x \n", mod_id);
+ else
+ bt_shell_printf("\tModel: %4.4x \n",
+ (uint16_t) (mod_id & 0xffff));
+ bt_shell_printf("\tApp Key Idx: %4.4x", pub->app_idx);
+ bt_shell_printf("\tTTL: %2.2x", pub->ttl);
+}
+
+void swap_u256_bytes(uint8_t *u256)
+{
+ int i;
+
+ /* End-to-End byte reflection of 32 octet buffer */
+ for (i = 0; i < 16; i++) {
+ u256[i] ^= u256[31 - i];
+ u256[31 - i] ^= u256[i];
+ u256[i] ^= u256[31 - i];
+ }
+}
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2017 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
+ *
+ */
+
+#include <stdbool.h>
+
+struct mesh_publication;
+
+#define OP_UNRELIABLE 0x0100
+
+void set_menu_prompt(const char *name, const char *id);
+void print_byte_array(const char *prefix, const void *ptr, int len);
+bool str2hex(const char *str, uint16_t in_len, uint8_t *out_buf,
+ uint16_t out_len);
+size_t hex2str(uint8_t *in, size_t in_len, char *out,
+ size_t out_len);
+uint16_t mesh_opcode_set(uint32_t opcode, uint8_t *buf);
+bool mesh_opcode_get(const uint8_t *buf, uint16_t sz, uint32_t *opcode, int *n);
+const char *mesh_status_str(uint8_t status);
+void print_model_pub(uint16_t ele_addr, uint32_t mod_id,
+ struct mesh_publication *pub);
+void swap_u256_bytes(uint8_t *u256);
#include <inttypes.h>
#include "lib/bluetooth.h"
+#include "lib/uuid.h"
#include "src/shared/util.h"
#include "bt.h"
print_field("%*cNameLength: 0x%04x (%u)", indent, ' ',
namelen, namelen);
- print_field("%*cName: ", indent, ' ');
+ printf("%*cName: ", indent+8, ' ');
for (; namelen > 0; namelen--) {
uint8_t c;
if (!l2cap_frame_get_u8(frame, &c))
printf("%1c", isprint(c) ? c : '.');
}
+ printf("\n");
return true;
}
for (; count > 0; count--) {
uint32_t attr;
- uint16_t charset;
- uint8_t len;
+ uint16_t charset, len;
if (!l2cap_frame_get_be32(frame, &attr))
return false;
print_field("%*cCharsetID: 0x%04x (%s)", indent, ' ',
charset, charset2str(charset));
- if (!l2cap_frame_get_u8(frame, &len))
+ if (!l2cap_frame_get_be16(frame, &len))
return false;
- print_field("%*cAttributeLength: 0x%02x (%u)", indent, ' ',
+ print_field("%*cAttributeLength: 0x%04x (%u)", indent, ' ',
len, len);
- print_field("%*cAttributeValue: ", indent, ' ');
+ printf("%*cAttributeValue: ", indent+8, ' ');
for (; len > 0; len--) {
uint8_t c;
printf("%1c", isprint(c) ? c : '.');
}
+ printf("\n");
}
return true;
print_field("%*cNameLength: 0x%04x (%u)", indent, ' ',
namelen, namelen);
- print_field("%*cName: ", indent, ' ');
+ printf("%*cName: ", indent+8, ' ');
for (; namelen > 0; namelen--) {
uint8_t c;
if (!l2cap_frame_get_u8(frame, &c))
printf("%1c", isprint(c) ? c : '.');
}
+ printf("\n");
if (!l2cap_frame_get_u8(frame, &count))
return false;
print_field("%*cMedia Type: %s (0x%02x)", 2, ' ',
mediatype2str(info >> 4), info >> 4);
print_field("%*cSEP Type: %s (0x%02x)", 2, ' ',
- info & 0x04 ? "SNK" : "SRC",
+ info & 0x08 ? "SNK" : "SRC",
(info >> 3) & 0x01);
print_field("%*cIn use: %s", 2, ' ',
seid & 0x02 ? "Yes" : "No");
ret = avdtp_signalling_packet(&avdtp_frame);
break;
default:
- packet_hexdump(frame->data, frame->size);
+ if (packet_has_filter(PACKET_FILTER_SHOW_A2DP_STREAM))
+ packet_hexdump(frame->data, frame->size);
return;
}
#include <inttypes.h>
#include "lib/bluetooth.h"
+#include "lib/uuid.h"
#include "src/shared/util.h"
#include "bt.h"
}
print_field("%*cDst: 0x%x(%s)", indent, ' ', dst_uuid,
- uuid32_to_str(dst_uuid));
+ bt_uuid32_to_str(dst_uuid));
print_field("%*cSrc: 0x%x(%s)", indent, ' ', src_uuid,
- uuid32_to_str(src_uuid));
+ bt_uuid32_to_str(src_uuid));
return true;
}
#endif
#include <stdio.h>
+#include <inttypes.h>
#include "src/shared/util.h"
#include "display.h"
#include "ll.h"
#include "vendor.h"
#include "broadcom.h"
+
+#define COLOR_UNKNOWN_FEATURE_BIT COLOR_WHITE_BG
+
#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
#include "uuid.h"
#endif
packet_print_error("Status", status);
}
+static void print_sco_routing(uint8_t routing)
+{
+ const char *str;
+
+ switch (routing) {
+ case 0x00:
+ str = "PCM";
+ break;
+ case 0x01:
+ str = "Transport";
+ break;
+ case 0x02:
+ str = "Codec";
+ break;
+ case 0x03:
+ str = "I2S";
+ break;
+ default:
+ str = "Reserved";
+ break;
+ }
+
+ print_field("SCO routing: %s (0x%2.2x)", str, routing);
+}
+
+static void print_pcm_interface_rate(uint8_t rate)
+{
+ const char *str;
+
+ switch (rate) {
+ case 0x00:
+ str = "128 KBps";
+ break;
+ case 0x01:
+ str = "256 KBps";
+ break;
+ case 0x02:
+ str = "512 KBps";
+ break;
+ case 0x03:
+ str = "1024 KBps";
+ break;
+ case 0x04:
+ str = "2048 KBps";
+ break;
+ default:
+ str = "Reserved";
+ break;
+ }
+
+ print_field("PCM interface rate: %s (0x%2.2x)", str, rate);
+}
+
+static void print_frame_type(uint8_t type)
+{
+ const char *str;
+
+ switch (type) {
+ case 0x00:
+ str = "Short";
+ break;
+ case 0x01:
+ str = "Long";
+ break;
+ default:
+ str = "Reserved";
+ break;
+ }
+
+ print_field("Frame type: %s (0x%2.2x)", str, type);
+}
+
+static void print_sync_mode(uint8_t mode)
+{
+ const char *str;
+
+ switch (mode) {
+ case 0x00:
+ str = "Slave";
+ break;
+ case 0x01:
+ str = "Master";
+ break;
+ default:
+ str = "Reserved";
+ break;
+ }
+
+ print_field("Sync mode: %s (0x%2.2x)", str, mode);
+}
+
+static void print_clock_mode(uint8_t mode)
+{
+ const char *str;
+
+ switch (mode) {
+ case 0x00:
+ str = "Slave";
+ break;
+ case 0x01:
+ str = "Master";
+ break;
+ default:
+ str = "Reserved";
+ break;
+ }
+
+ print_field("Clock mode: %s (0x%2.2x)", str, mode);
+}
+
+static void print_sleep_mode(uint8_t mode)
+{
+ const char *str;
+
+ switch (mode) {
+ case 0x00:
+ str = "No sleep mode";
+ break;
+ case 0x01:
+ str = "UART";
+ break;
+ case 0x02:
+ str = "UART with messaging";
+ break;
+ case 0x03:
+ str = "USB";
+ break;
+ case 0x04:
+ str = "H4IBSS";
+ break;
+ case 0x05:
+ str = "USB with Host wake";
+ break;
+ case 0x06:
+ str = "SDIO";
+ break;
+ case 0x07:
+ str = "UART CS-N";
+ break;
+ case 0x08:
+ str = "SPI";
+ break;
+ case 0x09:
+ str = "H5";
+ break;
+ case 0x0a:
+ str = "H4DS";
+ break;
+ case 0x0c:
+ str = "UART with BREAK";
+ break;
+ default:
+ str = "Reserved";
+ break;
+ }
+
+ print_field("Sleep mode: %s (0x%2.2x)", str, mode);
+}
+
+static void print_clock_setting(uint8_t clock)
+{
+ const char *str;
+
+ switch (clock) {
+ case 0x01:
+ str = "48 Mhz";
+ break;
+ case 0x02:
+ str = "24 Mhz";
+ break;
+ default:
+ str = "Reserved";
+ break;
+ }
+
+ print_field("UART clock: %s (0x%2.2x)", str, clock);
+}
+
static void null_cmd(const void *data, uint8_t size)
{
}
packet_print_addr("Address", data, false);
}
+static void update_uart_baud_rate_cmd(const void *data, uint8_t size)
+{
+ uint16_t enc_rate = get_le16(data);
+ uint32_t exp_rate = get_le32(data + 2);
+
+ if (enc_rate == 0x0000)
+ print_field("Encoded baud rate: Not used (0x0000)");
+ else
+ print_field("Encoded baud rate: 0x%4.4x", enc_rate);
+
+ print_field("Explicit baud rate: %u Mbps", exp_rate);
+}
+
+static void write_sco_pcm_int_param_cmd(const void *data, uint8_t size)
+{
+ uint8_t routing = get_u8(data);
+ uint8_t rate = get_u8(data + 1);
+ uint8_t frame_type = get_u8(data + 2);
+ uint8_t sync_mode = get_u8(data + 3);
+ uint8_t clock_mode = get_u8(data + 4);
+
+ print_sco_routing(routing);
+ print_pcm_interface_rate(rate);
+ print_frame_type(frame_type);
+ print_sync_mode(sync_mode);
+ print_clock_mode(clock_mode);
+}
+
+static void read_sco_pcm_int_param_rsp(const void *data, uint8_t size)
+{
+ uint8_t status = get_u8(data);
+ uint8_t routing = get_u8(data + 1);
+ uint8_t rate = get_u8(data + 2);
+ uint8_t frame_type = get_u8(data + 3);
+ uint8_t sync_mode = get_u8(data + 4);
+ uint8_t clock_mode = get_u8(data + 5);
+
+ print_status(status);
+ print_sco_routing(routing);
+ print_pcm_interface_rate(rate);
+ print_frame_type(frame_type);
+ print_sync_mode(sync_mode);
+ print_clock_mode(clock_mode);
+}
+
+static void set_sleepmode_param_cmd(const void *data, uint8_t size)
+{
+ uint8_t mode = get_u8(data);
+
+ print_sleep_mode(mode);
+
+ packet_hexdump(data + 1, size - 1);
+}
+
+static void read_sleepmode_param_rsp(const void *data, uint8_t size)
+{
+ uint8_t status = get_u8(data);
+ uint8_t mode = get_u8(data + 1);
+
+ print_status(status);
+ print_sleep_mode(mode);
+
+ packet_hexdump(data + 2, size - 2);
+}
+
+static void enable_radio_cmd(const void *data, uint8_t size)
+{
+ uint8_t mode = get_u8(data);
+ const char *str;
+
+ switch (mode) {
+ case 0x00:
+ str = "Disable the radio";
+ break;
+ case 0x01:
+ str = "Enable the radio";
+ break;
+ default:
+ str = "Reserved";
+ break;
+ }
+
+ print_field("Mode: %s (0x%2.2x)", str, mode);
+}
+
static void enable_usb_hid_emulation_cmd(const void *data, uint8_t size)
{
uint8_t enable = get_u8(data);
print_field("Enable: %s (0x%2.2x)", str, enable);
}
+static void read_uart_clock_setting_rsp(const void *data, uint8_t size)
+{
+ uint8_t status = get_u8(data);
+ uint8_t clock = get_u8(data + 1);
+
+ print_status(status);
+ print_clock_setting(clock);
+}
+
+static void write_uart_clock_setting_cmd(const void *data, uint8_t size)
+{
+ uint8_t clock = get_u8(data);
+
+ print_clock_setting(clock);
+}
+
static void write_ram_cmd(const void *data, uint8_t size)
{
uint32_t addr = get_le32(data);
packet_hexdump(data + 4, size - 4);
}
+static void read_ram_cmd(const void *data, uint8_t size)
+{
+ uint32_t addr = get_le32(data);
+ uint8_t length = get_u8(data + 4);
+
+ print_field("Address: 0x%8.8x", addr);
+ print_field("Length: %u", length);
+}
+
+static void read_ram_rsp(const void *data, uint8_t size)
+{
+ uint8_t status = get_u8(data);
+
+ print_status(status);
+
+ packet_hexdump(data + 1, size - 1);
+}
+
static void launch_ram_cmd(const void *data, uint8_t size)
{
uint32_t addr = get_le32(data);
print_field("Product: %4.4x:%4.4x", vid, pid);
}
+static const struct {
+ uint8_t bit;
+ const char *str;
+} features_table[] = {
+ { 0, "Multi-AV transport bandwidth reducer" },
+ { 1, "WBS SBC" },
+ { 2, "FW LC-PLC" },
+ { 3, "FM SBC internal stack" },
+ { }
+};
+
+static void print_features(const uint8_t *features_array)
+{
+ uint64_t mask, features = 0;
+ char str[41];
+ int i;
+
+ for (i = 0; i < 8; i++) {
+ sprintf(str + (i * 5), " 0x%2.2x", features_array[i]);
+ features |= ((uint64_t) features_array[i]) << (i * 8);
+ }
+
+ print_field("Features:%s", str);
+
+ mask = features;
+
+ for (i = 0; features_table[i].str; i++) {
+ if (features & (((uint64_t) 1) << features_table[i].bit)) {
+ print_field(" %s", features_table[i].str);
+ mask &= ~(((uint64_t) 1) << features_table[i].bit);
+ }
+ }
+
+ if (mask)
+ print_text(COLOR_UNKNOWN_FEATURE_BIT, " Unknown features "
+ "(0x%16.16" PRIx64 ")", mask);
+}
+
+static void read_controller_features_rsp(const void *data, uint8_t size)
+{
+ uint8_t status = get_u8(data);
+
+ print_status(status);
+ print_features(data + 1);
+}
+
static void read_verbose_version_info_rsp(const void *data, uint8_t size)
{
uint8_t status = get_u8(data);
{ 0x001, "Write BD ADDR",
write_bd_addr_cmd, 6, true,
status_rsp, 1, true },
- { 0x018, "Update UART Baud Rate" },
- { 0x027, "Set Sleepmode Param" },
+ { 0x018, "Update UART Baud Rate",
+ update_uart_baud_rate_cmd, 6, true,
+ status_rsp, 1, true },
+ { 0x01c, "Write SCO PCM Int Param",
+ write_sco_pcm_int_param_cmd, 5, true,
+ status_rsp, 1, true },
+ { 0x01d, "Read SCO PCM Int Param",
+ null_cmd, 0, true,
+ read_sco_pcm_int_param_rsp, 6, true },
+ { 0x027, "Set Sleepmode Param",
+ set_sleepmode_param_cmd, 12, true,
+ status_rsp, 1, true },
+ { 0x028, "Read Sleepmode Param",
+ null_cmd, 0, true,
+ read_sleepmode_param_rsp, 13, true },
{ 0x02e, "Download Minidriver",
null_cmd, 0, true,
status_rsp, 1, true },
+ { 0x034, "Enable Radio",
+ enable_radio_cmd, 1, true,
+ status_rsp, 1, true },
{ 0x03b, "Enable USB HID Emulation",
enable_usb_hid_emulation_cmd, 1, true,
status_rsp, 1, true },
- { 0x045, "Write UART Clock Setting" },
+ { 0x044, "Read UART Clock Setting",
+ null_cmd, 0, true,
+ read_uart_clock_setting_rsp, 1, true },
+ { 0x045, "Write UART Clock Setting",
+ write_uart_clock_setting_cmd, 1, true,
+ status_rsp, 1, true },
{ 0x04c, "Write RAM",
write_ram_cmd, 4, false,
status_rsp, 1, true },
+ { 0x04d, "Read RAM",
+ read_ram_cmd, 5, true,
+ read_ram_rsp, 1, false },
{ 0x04e, "Launch RAM",
launch_ram_cmd, 4, true,
status_rsp, 1, true },
{ 0x05a, "Read VID PID",
null_cmd, 0, true,
read_vid_pid_rsp, 5, true },
+ { 0x057, "Write High Priority Connection" },
+ { 0x06d, "Write I2SPCM Interface Param" },
+ { 0x06e, "Read Controller Features",
+ null_cmd, 0, true,
+ read_controller_features_rsp, 9, true },
{ 0x079, "Read Verbose Config Version Info",
null_cmd, 0, true,
read_verbose_version_info_rsp, 7, true },
uint16_t max_rx_time;
} __attribute__ ((packed));
+#define BT_HCI_CMD_LE_READ_PHY 0x2030
+struct bt_hci_cmd_le_read_phy {
+ uint16_t handle;
+} __attribute__((packed));
+struct bt_hci_rsp_le_read_phy {
+ uint8_t status;
+ uint16_t handle;
+ uint8_t tx_phy;
+ uint8_t rx_phy;
+} __attribute__((packed));
+
+#define BT_HCI_CMD_LE_SET_DEFAULT_PHY 0x2031
+struct bt_hci_cmd_le_set_default_phy {
+ uint8_t all_phys;
+ uint8_t tx_phys;
+ uint8_t rx_phys;
+} __attribute__((packed));
+
+#define BT_HCI_CMD_LE_SET_PHY 0x2032
+struct bt_hci_cmd_le_set_phy {
+ uint16_t handle;
+ uint8_t all_phys;
+ uint8_t tx_phys;
+ uint8_t rx_phys;
+ uint16_t phy_opts;
+} __attribute__((packed));
+
+#define BT_HCI_CMD_LE_ENHANCED_RECEIVER_TEST 0x2033
+struct bt_hci_cmd_le_enhanced_receiver_test {
+ uint8_t rx_channel;
+ uint8_t phy;
+ uint8_t modulation_index;
+} __attribute__((packed));
+
+#define BT_HCI_CMD_LE_ENHANCED_TRANSMITTER_TEST 0x2034
+struct bt_hci_cmd_le_enhanced_transmitter_test {
+ uint8_t tx_channel;
+ uint8_t data_len;
+ uint8_t payload;
+ uint8_t phy;
+} __attribute__((packed));
+
+#define BT_HCI_CMD_LE_SET_ADV_SET_RAND_ADDR 0x2035
+struct bt_hci_cmd_le_set_adv_set_rand_addr {
+ uint8_t handle;
+ uint8_t bdaddr[6];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_SET_EXT_ADV_PARAMS 0x2036
+struct bt_hci_cmd_le_set_ext_adv_params {
+ uint8_t handle;
+ uint16_t evt_properties;
+ uint8_t min_interval[3];
+ uint8_t max_interval[3];
+ uint8_t channel_map;
+ uint8_t own_addr_type;
+ uint8_t peer_addr_type;
+ uint8_t peer_addr[6];
+ uint8_t filter_policy;
+ uint8_t tx_power;
+ uint8_t primary_phy;
+ uint8_t secondary_max_skip;
+ uint8_t secondary_phy;
+ uint8_t sid;
+ uint8_t notif_enable;
+} __attribute__ ((packed));
+struct bt_hci_rsp_le_set_ext_adv_params {
+ uint8_t status;
+ uint8_t tx_power;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_SET_EXT_ADV_DATA 0x2037
+struct bt_hci_cmd_le_set_ext_adv_data {
+ uint8_t handle;
+ uint8_t operation;
+ uint8_t fragment_preference;
+ uint8_t data_len;
+ uint8_t data[0];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_SET_EXT_SCAN_RSP_DATA 0x2038
+struct bt_hci_cmd_le_set_ext_scan_rsp_data {
+ uint8_t handle;
+ uint8_t operation;
+ uint8_t fragment_preference;
+ uint8_t data_len;
+ uint8_t data[0];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_SET_EXT_ADV_ENABLE 0x2039
+struct bt_hci_cmd_le_set_ext_adv_enable {
+ uint8_t enable;
+ uint8_t num_of_sets;
+} __attribute__ ((packed));
+struct bt_hci_cmd_ext_adv_set {
+ uint8_t handle;
+ uint16_t duration;
+ uint8_t max_events;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_READ_MAX_ADV_DATA_LEN 0x203a
+struct bt_hci_rsp_le_read_max_adv_data_len {
+ uint8_t status;
+ uint16_t max_len;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_READ_NUM_SUPPORTED_ADV_SETS 0x203b
+struct bt_hci_rsp_le_read_num_supported_adv_sets {
+ uint8_t status;
+ uint8_t num_of_sets;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_REMOVE_ADV_SET 0x203c
+struct bt_hci_cmd_le_remove_adv_set {
+ uint8_t handle;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_CLEAR_ADV_SETS 0x203d
+
+#define BT_HCI_CMD_LE_SET_PERIODIC_ADV_PARAMS 0x203e
+struct bt_hci_cmd_le_set_periodic_adv_params {
+ uint8_t handle;
+ uint16_t min_interval;
+ uint16_t max_interval;
+ uint16_t properties;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_SET_PERIODIC_ADV_DATA 0x203f
+struct bt_hci_cmd_le_set_periodic_adv_data {
+ uint8_t handle;
+ uint8_t operation;
+ uint8_t data_len;
+ uint8_t data[0];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_SET_PERIODIC_ADV_ENABLE 0x2040
+struct bt_hci_cmd_le_set_periodic_adv_enable {
+ uint8_t enable;
+ uint8_t handle;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_SET_EXT_SCAN_PARAMS 0x2041
+struct bt_hci_cmd_le_set_ext_scan_params {
+ uint8_t own_addr_type;
+ uint8_t filter_policy;
+ uint8_t num_phys;
+ uint8_t data[0];
+} __attribute__ ((packed));
+struct bt_hci_le_scan_phy {
+ uint8_t type;
+ uint16_t interval;
+ uint16_t window;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_SET_EXT_SCAN_ENABLE 0x2042
+struct bt_hci_cmd_le_set_ext_scan_enable {
+ uint8_t enable;
+ uint8_t filter_dup;
+ uint16_t duration;
+ uint16_t period;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_EXT_CREATE_CONN 0x2043
+struct bt_hci_cmd_le_ext_create_conn {
+ uint8_t filter_policy;
+ uint8_t own_addr_type;
+ uint8_t peer_addr_type;
+ uint8_t peer_addr[6];
+ uint8_t phys;
+ uint8_t data[0];
+} __attribute__ ((packed));
+struct bt_hci_le_ext_create_conn {
+ uint16_t scan_interval;
+ uint16_t scan_window;
+ uint16_t min_interval;
+ uint16_t max_interval;
+ uint16_t latency;
+ uint16_t supv_timeout;
+ uint16_t min_length;
+ uint16_t max_length;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_PERIODIC_ADV_CREATE_SYNC 0x2044
+struct bt_hci_cmd_le_periodic_adv_create_sync {
+ uint8_t filter_policy;
+ uint8_t sid;
+ uint8_t addr_type;
+ uint8_t addr[6];
+ uint16_t skip;
+ uint16_t sync_timeout;
+ uint8_t unused;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_PERIODIC_ADV_CREATE_SYNC_CANCEL 0x2045
+
+#define BT_HCI_CMD_LE_PERIODIC_ADV_TERM_SYNC 0x2046
+struct bt_hci_cmd_le_periodic_adv_term_sync {
+ uint16_t sync_handle;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_ADD_DEV_PERIODIC_ADV_LIST 0x2047
+struct bt_hci_cmd_le_add_dev_periodic_adv_list {
+ uint8_t addr_type;
+ uint8_t addr[6];
+ uint8_t sid;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_REMOVE_DEV_PERIODIC_ADV_LIST 0x2048
+struct bt_hci_cmd_le_remove_dev_periodic_adv_list {
+ uint8_t addr_type;
+ uint8_t addr[6];
+ uint8_t sid;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_CLEAR_PERIODIC_ADV_LIST 0x2049
+
+#define BT_HCI_CMD_LE_READ_PERIODIC_ADV_LIST_SIZE 0x204a
+struct bt_hci_rsp_le_read_dev_periodic_adv_list_size {
+ uint8_t status;
+ uint8_t list_size;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_READ_TX_POWER 0x204b
+struct bt_hci_rsp_le_read_tx_power {
+ uint8_t status;
+ uint8_t min_tx_power;
+ uint8_t max_tx_power;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_READ_RF_PATH_COMPENSATION 0x204c
+struct bt_hci_rsp_le_read_rf_path_comp {
+ uint8_t status;
+ uint16_t rf_tx_path_comp;
+ uint16_t rf_rx_path_comp;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_WRITE_RF_PATH_COMPENSATION 0x204d
+struct bt_hci_cmd_le_write_rf_path_comp {
+ uint16_t rf_tx_path_comp;
+ uint16_t rf_rx_path_comp;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_SET_PRIV_MODE 0x204e
+struct bt_hci_cmd_le_set_priv_mode {
+ uint8_t peer_id_addr_type;
+ uint8_t peer_id_addr[6];
+ uint8_t priv_mode;
+} __attribute__ ((packed));
+
#define BT_HCI_EVT_INQUIRY_COMPLETE 0x01
struct bt_hci_evt_inquiry_complete {
uint8_t status;
int8_t rssi;
} __attribute__ ((packed));
+#define BT_HCI_EVT_LE_PHY_UPDATE_COMPLETE 0x0c
+struct bt_hci_evt_le_phy_update_complete {
+ uint8_t status;
+ uint16_t handle;
+ uint8_t tx_phy;
+ uint8_t rx_phy;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_LE_EXT_ADV_REPORT 0x0d
+struct bt_hci_evt_le_ext_adv_report {
+ uint8_t num_reports;
+} __attribute__ ((packed));
+struct bt_hci_le_ext_adv_report {
+ uint16_t event_type;
+ uint8_t addr_type;
+ uint8_t addr[6];
+ uint8_t primary_phy;
+ uint8_t secondary_phy;
+ uint8_t sid;
+ uint8_t tx_power;
+ int8_t rssi;
+ uint16_t interval;
+ uint8_t direct_addr_type;
+ uint8_t direct_addr[6];
+ uint8_t data_len;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_LE_ADV_SET_TERM 0x12
+struct bt_hci_evt_le_adv_set_term {
+ uint8_t status;
+ uint8_t handle;
+ uint16_t conn_handle;
+ uint8_t num_evts;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_LE_SCAN_REQ_RECEIVED 0x13
+struct bt_hci_evt_le_scan_req_received {
+ uint8_t handle;
+ uint8_t scanner_addr_type;
+ uint8_t scanner_addr[6];
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_LE_CHAN_SELECT_ALG 0x14
+struct bt_hci_evt_le_chan_select_alg {
+ uint16_t handle;
+ uint8_t algorithm;
+} __attribute__ ((packed));
+
#define BT_HCI_ERR_SUCCESS 0x00
#define BT_HCI_ERR_UNKNOWN_COMMAND 0x01
#define BT_HCI_ERR_UNKNOWN_CONN_ID 0x02
void control_server(const char *path)
{
struct sockaddr_un addr;
+ size_t len;
int fd;
if (server_fd >= 0)
return;
+ len = strlen(path);
+ if (len > sizeof(addr.sun_path) - 1) {
+ fprintf(stderr, "Socket name too long\n");
+ return;
+ }
+
unlink(path);
fd = socket(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
- strcpy(addr.sun_path, path);
+ strncpy(addr.sun_path, path, len);
if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
perror("Failed to bind server socket");
print_field("Firmware patch: %u", fw_patch);
}
+static void set_uart_baudrate_cmd(const void *data, uint8_t size)
+{
+ uint8_t baudrate = get_u8(data);
+ const char *str;
+
+ switch (baudrate) {
+ case 0x00:
+ str = "9600 Baud";
+ break;
+ case 0x01:
+ str = "19200 Baud";
+ break;
+ case 0x02:
+ str = "38400 Baud";
+ break;
+ case 0x03:
+ str = "57600 Baud";
+ break;
+ case 0x04:
+ str = "115200 Baud";
+ break;
+ case 0x05:
+ str = "230400 Baud";
+ break;
+ case 0x06:
+ str = "460800 Baud";
+ break;
+ case 0x07:
+ str = "921600 Baud";
+ break;
+ case 0x08:
+ str = "1843200 Baud";
+ break;
+ case 0x09:
+ str = "3250000 baud";
+ break;
+ case 0x0a:
+ str = "2000000 baud";
+ break;
+ case 0x0b:
+ str = "3000000 baud";
+ break;
+ case 0x0c:
+ str = "3714286 baud";
+ break;
+ case 0x0d:
+ str = "4333333 baud";
+ break;
+ case 0x0e:
+ str = "6500000 baud";
+ break;
+ default:
+ str = "Reserved";
+ break;
+ }
+
+ print_field("Baudrate: %s (0x%2.2x)", str, baudrate);
+}
+
static void secure_send_cmd(const void *data, uint8_t size)
{
uint8_t type = get_u8(data);
{ 0x005, "Read Version",
null_cmd, 0, true,
read_version_rsp, 10, true },
- { 0x006, "Set UART Baudrate" },
+ { 0x006, "Set UART Baudrate",
+ set_uart_baudrate_cmd, 1, true,
+ status_rsp, 1, true },
{ 0x007, "Enable LPM" },
{ 0x008, "PCM Write Configuration" },
{ 0x009, "Secure Send",
#include <inttypes.h>
#include "lib/bluetooth.h"
+#include "lib/uuid.h"
#include "src/shared/util.h"
#include "bt.h"
#include "packet.h"
#include "display.h"
#include "l2cap.h"
-#include "uuid.h"
#include "keys.h"
#include "sdp.h"
#include "avctp.h"
case 0x0009:
str = "Connection refused - Invalid Source CID";
break;
- case 0x0010:
+ case 0x000a:
str = "Connection refused - Source CID already allocated";
break;
+ case 0x000b:
+ str = "Connection refused - unacceptable parameters";
+ break;
default:
str = "Reserved";
break;
switch (size) {
case 2:
- str = uuid16_to_str(get_le16(data));
+ str = bt_uuid16_to_str(get_le16(data));
print_field("%s: %s (0x%4.4x)", label, str, get_le16(data));
break;
case 4:
- str = uuid32_to_str(get_le32(data));
+ str = bt_uuid32_to_str(get_le32(data));
print_field("%s: %s (0x%8.8x)", label, str, get_le32(data));
break;
case 16:
get_le32(data + 12), get_le16(data + 10),
get_le16(data + 8), get_le16(data + 6),
get_le32(data + 2), get_le16(data + 0));
- str = uuidstr_to_str(uuidstr);
+ str = bt_uuidstr_to_str(uuidstr);
print_field("%s: %s (%s)", label, str, uuidstr);
break;
default:
static void print_attribute_info(uint16_t type, const void *data, uint16_t len)
{
- const char *str = uuid16_to_str(type);
+ const char *str = bt_uuid16_to_str(type);
print_field("%s: %s (0x%4.4x)", "Attribute type", str, type);
static void print_smp_auth_req(uint8_t auth_req)
{
- const char *bond, *mitm, *sc, *kp;
+ const char *bond, *mitm, *sc, *kp, *ct2;
switch (auth_req & 0x03) {
case 0x00:
break;
}
- if ((auth_req & 0x04))
+ if (auth_req & 0x04)
mitm = "MITM";
else
mitm = "No MITM";
- if ((auth_req & 0x08))
+ if (auth_req & 0x08)
sc = "SC";
else
sc = "Legacy";
- if ((auth_req & 0x10))
+ if (auth_req & 0x10)
kp = "Keypresses";
else
kp = "No Keypresses";
- print_field("Authentication requirement: %s, %s, %s, %s (0x%2.2x)",
- bond, mitm, sc, kp, auth_req);
+ if (auth_req & 0x20)
+ ct2 = ", CT2";
+ else
+ ct2 = "";
+
+ print_field("Authentication requirement: %s, %s, %s, %s%s (0x%2.2x)",
+ bond, mitm, sc, kp, ct2, auth_req);
}
static void print_smp_key_dist(const char *label, uint8_t dist)
{ 0x13, "LL_PING_RSP", null_pdu, 0, true },
{ 0x14, "LL_LENGTH_REQ", NULL, 8, true },
{ 0x15, "LL_LENGTH_RSP", NULL, 8, true },
+ { 0x16, "LL_PHY_REQ", NULL, 2, true },
+ { 0x17, "LL_PHY_RSP", NULL, 2, true },
+ { 0x18, "LL_PHY_UPDATE_IND", NULL, 4, true },
+ { 0x19, "LL_MIN_USED_CHANNELS_IND", NULL, 2, true },
{ }
};
{ LMP_ESC4(32), "LMP_power_control_res", power_control_res, 1, true },
{ LMP_ESC4(33), "LMP_ping_req", ping_req, 0, true },
{ LMP_ESC4(34), "LMP_ping_res", ping_res, 0, true },
+ { LMP_ESC4(35), "LMP_SAM_set_type0" },
+ { LMP_ESC4(36), "LMP_SAM_define_map" },
+ { LMP_ESC4(37), "LMP_SAM_switch" },
{ }
};
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
+#include <sys/un.h>
#include "src/shared/mainloop.h"
#include "src/shared/tty.h"
"\t-t, --time Show time instead of time offset\n"
"\t-T, --date Show time and date information\n"
"\t-S, --sco Dump SCO traffic\n"
+ "\t-A, --a2dp Dump A2DP stream traffic\n"
"\t-E, --ellisys [ip] Send Ellisys HCI Injection\n"
#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
"\t-C, --count <num> Save traces by <num> rotation\n"
{ "time", no_argument, NULL, 't' },
{ "date", no_argument, NULL, 'T' },
{ "sco", no_argument, NULL, 'S' },
+ { "a2dp", no_argument, NULL, 'A' },
{ "ellisys", required_argument, NULL, 'E' },
#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
{ "count", required_argument, NULL, 'C' },
for (;;) {
int opt;
+ struct sockaddr_un addr;
#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
opt = getopt_long(argc, argv, "d:r:w:a:s:p:i:tTSE:C:W:vh",
main_options, NULL);
#else
- opt = getopt_long(argc, argv, "d:r:w:a:s:p:i:tTSE:vh",
+ opt = getopt_long(argc, argv, "d:r:w:a:s:p:i:tTSAE:vh",
main_options, NULL);
#endif
if (opt < 0)
analyze_path = optarg;
break;
case 's':
+ if (strlen(optarg) > sizeof(addr.sun_path) - 1) {
+ fprintf(stderr, "Socket name too long\n");
+ return EXIT_FAILURE;
+ }
control_server(optarg);
break;
case 'p':
case 'S':
filter_mask |= PACKET_FILTER_SHOW_SCO_DATA;
break;
+ case 'A':
+ filter_mask |= PACKET_FILTER_SHOW_A2DP_STREAM;
+ break;
case 'E':
ellisys_server = optarg;
ellisys_port = 24352;
#include <sys/socket.h>
#include "lib/bluetooth.h"
+#include "lib/uuid.h"
#include "lib/hci.h"
#include "lib/hci_lib.h"
#include "ll.h"
#include "hwdb.h"
#include "keys.h"
-#include "uuid.h"
#include "l2cap.h"
#include "control.h"
#include "vendor.h"
#include "packet.h"
#define COLOR_CHANNEL_LABEL COLOR_WHITE
+#define COLOR_FRAME_LABEL COLOR_WHITE
#define COLOR_INDEX_LABEL COLOR_WHITE
#define COLOR_TIMESTAMP COLOR_YELLOW
return 0xff;
}
+bool packet_has_filter(unsigned long filter)
+{
+ return filter_mask & filter;
+}
+
void packet_set_filter(unsigned long filter)
{
filter_mask = filter;
#define print_space(x) printf("%*c", (x), ' ');
+#define MAX_INDEX 16
+
+struct index_data {
+ uint8_t type;
+ uint8_t bdaddr[6];
+ uint16_t manufacturer;
+ size_t frame;
+};
+
+static struct index_data index_list[MAX_INDEX];
+
static void print_packet(struct timeval *tv, struct ucred *cred, char ident,
uint16_t index, const char *channel,
const char *color, const char *label,
int col = num_columns();
char line[256], ts_str[96];
int n, ts_len = 0, ts_pos = 0, len = 0, pos = 0;
+ static size_t last_frame;
if (channel) {
if (use_color()) {
ts_pos += n;
ts_len += n;
}
+ } else if (index != HCI_DEV_NONE &&
+ index_list[index].frame != last_frame) {
+ if (use_color()) {
+ n = sprintf(ts_str + ts_pos, "%s", COLOR_FRAME_LABEL);
+ if (n > 0)
+ ts_pos += n;
+ }
+
+ n = sprintf(ts_str + ts_pos, " #%zu", index_list[index].frame);
+ if (n > 0) {
+ ts_pos += n;
+ ts_len += n;
+ }
+ last_frame = index_list[index].frame;
}
if ((filter_mask & PACKET_FILTER_SHOW_INDEX) &&
{ 0x39, "Connection Rejected due to No Suitable Channel Found" },
{ 0x3a, "Controller Busy" },
{ 0x3b, "Unacceptable Connection Parameters" },
- { 0x3c, "Directed Advertising Timeout" },
+ { 0x3c, "Advertising Timeout" },
{ 0x3d, "Connection Terminated due to MIC Failure" },
{ 0x3e, "Connection Failed to be Established" },
{ 0x3f, "MAC Connection Failed" },
{ 0x40, "Coarse Clock Adjustment Rejected "
"but Will Try to Adjust Using Clock Dragging" },
+ { 0x41, "Type0 Submap Not Defined" },
+ { 0x42, "Unknown Advertising Identifier" },
+ { 0x43, "Limit Reached" },
+ { 0x44, "Operation Cancelled by Host" },
{ }
};
print_error(label, error);
}
+static void print_enable(const char *label, uint8_t enable)
+{
+ const char *str;
+
+ switch (enable) {
+ case 0x00:
+ str = "Disabled";
+ break;
+ case 0x01:
+ str = "Enabled";
+ break;
+ default:
+ str = "Reserved";
+ break;
+ }
+
+ print_field("%s: %s (0x%2.2x)", label, str, enable);
+}
+
static void print_addr_type(const char *label, uint8_t addr_type)
{
const char *str;
" Unknown service class (0x%2.2x)", mask);
}
-static const struct {
- uint16_t val;
- bool generic;
- const char *str;
-} appearance_table[] = {
- { 0, true, "Unknown" },
- { 64, true, "Phone" },
- { 128, true, "Computer" },
- { 192, true, "Watch" },
- { 193, false, "Sports Watch" },
- { 256, true, "Clock" },
- { 320, true, "Display" },
- { 384, true, "Remote Control" },
- { 448, true, "Eye-glasses" },
- { 512, true, "Tag" },
- { 576, true, "Keyring" },
- { 640, true, "Media Player" },
- { 704, true, "Barcode Scanner" },
- { 768, true, "Thermometer" },
- { 769, false, "Thermometer: Ear" },
- { 832, true, "Heart Rate Sensor" },
- { 833, false, "Heart Rate Belt" },
- { 896, true, "Blood Pressure" },
- { 897, false, "Blood Pressure: Arm" },
- { 898, false, "Blood Pressure: Wrist" },
- { 960, true, "Human Interface Device" },
- { 961, false, "Keyboard" },
- { 962, false, "Mouse" },
- { 963, false, "Joystick" },
- { 964, false, "Gamepad" },
- { 965, false, "Digitizer Tablet" },
- { 966, false, "Card Reader" },
- { 967, false, "Digital Pen" },
- { 968, false, "Barcode Scanner" },
- { 1024, true, "Glucose Meter" },
- { 1088, true, "Running Walking Sensor" },
- { 1089, false, "Running Walking Sensor: In-Shoe" },
- { 1090, false, "Running Walking Sensor: On-Shoe" },
- { 1091, false, "Running Walking Sensor: On-Hip" },
- { 1152, true, "Cycling" },
- { 1153, false, "Cycling: Cycling Computer" },
- { 1154, false, "Cycling: Speed Sensor" },
- { 1155, false, "Cycling: Cadence Sensor" },
- { 1156, false, "Cycling: Power Sensor" },
- { 1157, false, "Cycling: Speed and Cadence Sensor" },
- { 1216, true, "Undefined" },
-
- { 3136, true, "Pulse Oximeter" },
- { 3137, false, "Pulse Oximeter: Fingertip" },
- { 3138, false, "Pulse Oximeter: Wrist Worn" },
- { 3200, true, "Weight Scale" },
- { 3264, true, "Undefined" },
-
- { 5184, true, "Outdoor Sports Activity" },
- { 5185, false, "Location Display Device" },
- { 5186, false, "Location and Navigation Display Device" },
- { 5187, false, "Location Pod" },
- { 5188, false, "Location and Navigation Pod" },
- { 5248, true, "Undefined" },
- { }
-};
-
static void print_appearance(uint16_t appearance)
{
- const char *str = NULL;
- int i, type = 0;
-
- for (i = 0; appearance_table[i].str; i++) {
- if (appearance_table[i].generic) {
- if (appearance < appearance_table[i].val)
- break;
- type = i;
- }
-
- if (appearance_table[i].val == appearance) {
- str = appearance_table[i].str;
- break;
- }
- }
-
- if (!str)
- str = appearance_table[type].str;
-
- print_field("Appearance: %s (0x%4.4x)", str, appearance);
+ print_field("Appearance: %s (0x%4.4x)", bt_appear_to_str(appearance),
+ appearance);
}
static void print_num_broadcast_retrans(uint8_t num_retrans)
static void print_power_level(int8_t level, const char *type)
{
- print_field("TX power%s%s%s: %d dBm",
- type ? " (" : "", type ? type : "", type ? ")" : "", level);
-}
-
-static void print_sync_flow_control(uint8_t enable)
-{
- const char *str;
-
- switch (enable) {
- case 0x00:
- str = "Disabled";
- break;
- case 0x01:
- str = "Enabled";
- break;
- default:
- str = "Reserved";
- break;
- }
-
- print_field("Flow control: %s (0x%2.2x)", str, enable);
+ print_field("TX power%s%s%s: %d dbm (0x%2.2x)",
+ type ? " (" : "", type ? type : "", type ? ")" : "",
+ level, level);
}
static void print_host_flow_control(uint8_t enable)
print_field("Type: %s (0x%2.2x)", str, type);
}
-static void print_afh_mode(uint8_t mode)
-{
- const char *str;
-
- switch (mode) {
- case 0x00:
- str = "Disabled";
- break;
- case 0x01:
- str = "Enabled";
- break;
- default:
- str = "Reserved";
- break;
- }
-
- print_field("Mode: %s (0x%2.2x)", str, mode);
-}
-
-static void print_erroneous_reporting(uint8_t mode)
-{
- const char *str;
-
- switch (mode) {
- case 0x00:
- str = "Disabled";
- break;
- case 0x01:
- str = "Enabled";
- break;
- default:
- str = "Reserved";
- break;
- }
-
- print_field("Mode: %s (0x%2.2x)", str, mode);
-}
-
static void print_loopback_mode(uint8_t mode)
{
const char *str;
print_field("Mode: %s (0x%2.2x)", str, mode);
}
-static void print_simple_pairing_mode(uint8_t mode)
-{
- const char *str;
-
- switch (mode) {
- case 0x00:
- str = "Disabled";
- break;
- case 0x01:
- str = "Enabled";
- break;
- default:
- str = "Reserved";
- break;
- }
-
- print_field("Mode: %s (0x%2.2x)", str, mode);
-}
-
-static void print_ssp_debug_mode(uint8_t mode)
-{
- const char *str;
-
- switch (mode) {
- case 0x00:
- str = "Disabled";
- break;
- case 0x01:
- str = "Enabled";
- break;
- default:
- str = "Reserved";
- break;
- }
-
- print_field("Debug mode: %s (0x%2.2x)", str, mode);
-}
-
-static void print_secure_conn_support(uint8_t support)
-{
- const char *str;
-
- switch (support) {
- case 0x00:
- str = "Disabled";
- break;
- case 0x01:
- str = "Enabled";
- break;
- default:
- str = "Reserved";
- break;
- }
-
- print_field("Support: %s (0x%2.2x)", str, support);
-}
-
static void print_auth_payload_timeout(uint16_t timeout)
{
print_field("Timeout: %d msec (0x%4.4x)",
print_field("Link type: %s (0x%2.2x)", str, link_type);
}
-static void print_encr_mode(uint8_t encr_mode)
-{
- const char *str;
-
- switch (encr_mode) {
- case 0x00:
- str = "Disabled";
- break;
- case 0x01:
- str = "Enabled";
- break;
- default:
- str = "Reserved";
- break;
- }
-
- print_field("Encryption: %s (0x%2.2x)", str, encr_mode);
-}
-
static void print_encr_mode_change(uint8_t encr_mode, uint16_t handle)
{
const char *str;
print_field(" Flush timeout: 0x%8.8x", get_le32(data + 12));
}
-static void print_short_range_mode(uint8_t mode)
-{
- const char *str;
-
- switch (mode) {
- case 0x00:
- str = "Disabled";
- break;
- case 0x01:
- str = "Enabled";
- break;
- default:
- str = "Reserved";
- break;
- }
-
- print_field("Short range mode: %s (0x%2.2x)", str, mode);
-}
-
static void print_amp_status(uint8_t amp_status)
{
const char *str;
print_field("Num reports: %d", num_reports);
}
-static void print_adv_event_type(uint8_t type)
+static void print_adv_event_type(const char *label, uint8_t type)
{
const char *str;
break;
}
- print_field("Event type: %s (0x%2.2x)", str, type);
+ print_field("%s: %s (0x%2.2x)", label, str, type);
+}
+
+static void print_adv_channel_map(const char *label, uint8_t value)
+{
+ const char *str;
+
+ switch (value) {
+ case 0x01:
+ str = "37";
+ break;
+ case 0x02:
+ str = "38";
+ break;
+ case 0x03:
+ str = "37, 38";
+ break;
+ case 0x04:
+ str = "39";
+ break;
+ case 0x05:
+ str = "37, 39";
+ break;
+ case 0x06:
+ str = "38, 39";
+ break;
+ case 0x07:
+ str = "37, 38, 39";
+ break;
+ default:
+ str = "Reserved";
+ break;
+ }
+
+ print_field("%s: %s (0x%2.2x)", label, str, value);
+}
+
+static void print_adv_filter_policy(const char *label, uint8_t value)
+{
+ const char *str;
+
+ switch (value) {
+ case 0x00:
+ str = "Allow Scan Request from Any, "
+ "Allow Connect Request from Any";
+ break;
+ case 0x01:
+ str = "Allow Scan Request from White List Only, "
+ "Allow Connect Request from Any";
+ break;
+ case 0x02:
+ str = "Allow Scan Request from Any, "
+ "Allow Connect Request from White List Only";
+ break;
+ case 0x03:
+ str = "Allow Scan Request from White List Only, "
+ "Allow Connect Request from White List Only";
+ break;
+ default:
+ str = "Reserved";
+ break;
+ }
+
+ print_field("%s: %s (0x%2.2x)", label, str, value);
}
static void print_rssi(int8_t rssi)
print_slot_625("Window", window);
}
+static void print_conn_latency(const char *label, uint16_t value)
+{
+ print_field("%s: %u (0x%4.4x)", label, le16_to_cpu(value),
+ le16_to_cpu(value));
+}
+
static void print_role(uint8_t role)
{
const char *str;
case 0x08:
str = "Bluetooth 4.2";
break;
+ case 0x09:
+ str = "Bluetooth 5.0";
+ break;
default:
str = "Reserved";
break;
{ 6, "Coarse Clock Adjustment" },
{ 8, "Secure Connections (Controller Support)" },
{ 9, "Ping" },
+ { 10, "Slot Availability Mask" },
{ 11, "Train nudging" },
{ }
};
{ 5, "LE Data Packet Length Extension" },
{ 6, "LL Privacy" },
{ 7, "Extended Scanner Filter Policies" },
+ { 8, "LE 2M PHY" },
+ { 9, "Stable Modulation Index - Transmitter" },
+ { 10, "Stable Modulation Index - Receiver" },
+ { 11, "LE Coded PHY" },
+ { 12, "LE Extended Advertising" },
+ { 13, "LE Periodic Advertising" },
+ { 14, "Channel Selection Algorithm #2" },
+ { 15, "LE Power Class 1" },
+ { 16, "Minimum Number of Used Channels Procedure"},
{ }
};
{ 21, "Connectionless Slave Broadcast Channel Map Change" },
{ 22, "Inquiry Response Notification" },
{ 23, "Authenticated Payload Timeout Expired" },
+ { 24, "SAM Status Change" },
{ }
};
{ 8, "LE Generate DHKey Complete" },
{ 9, "LE Enhanced Connection Complete" },
{ 10, "LE Direct Advertising Report" },
+ { 11, "LE PHY Update Complete" },
+ { 12, "LE Extended Advertising Report" },
+ { 13, "LE Periodic Advertising Sync Established"},
+ { 14, "LE Periodic Advertising Report" },
+ { 15, "LE Periodic Advertising Sync Lost" },
+ { 16, "LE Extended Scan Timeout" },
+ { 17, "LE Extended Advertising Set Terminated" },
+ { 18, "LE Scan Request Received" },
+ { 19, "LE Channel Selection Algorithm" },
{ }
};
#define BT_EIR_LE_ROLE 0x1c
#define BT_EIR_SSP_HASH_P256 0x1d
#define BT_EIR_SSP_RANDOMIZER_P256 0x1e
+#define BT_EIR_SERVICE_UUID32 0x1f
+#define BT_EIR_SERVICE_DATA32 0x20
+#define BT_EIR_SERVICE_DATA128 0x21
+#define BT_EIR_LE_SC_CONFIRM_VALUE 0x22
+#define BT_EIR_LE_SC_RANDOM_VALUE 0x23
+#define BT_EIR_URI 0x24
+#define BT_EIR_INDOOR_POSITIONING 0x25
+#define BT_EIR_TRANSPORT_DISCOVERY 0x26
+#define BT_EIR_LE_SUPPORTED_FEATURES 0x27
+#define BT_EIR_CHANNEL_MAP_UPDATE_IND 0x28
+#define BT_EIR_MESH_PROV 0x29
+#define BT_EIR_MESH_DATA 0x2a
+#define BT_EIR_MESH_BEACON 0x2b
#define BT_EIR_3D_INFO_DATA 0x3d
#define BT_EIR_MANUFACTURER_DATA 0xff
for (i = 0; i < count; i++) {
uint16_t uuid = get_le16(data + (i * 2));
- print_field(" %s (0x%4.4x)", uuid16_to_str(uuid), uuid);
+ print_field(" %s (0x%4.4x)", bt_uuid16_to_str(uuid), uuid);
}
}
for (i = 0; i < count; i++) {
uint32_t uuid = get_le32(data + (i * 4));
- print_field(" %s (0x%8.8x)", uuid32_to_str(uuid), uuid);
+ print_field(" %s (0x%8.8x)", bt_uuid32_to_str(uuid), uuid);
}
}
get_le32(&uuid[12]), get_le16(&uuid[10]),
get_le16(&uuid[8]), get_le16(&uuid[6]),
get_le32(&uuid[2]), get_le16(&uuid[0]));
- print_field(" %s (%s)", uuidstr_to_str(uuidstr), uuidstr);
+ print_field(" %s (%s)", bt_uuidstr_to_str(uuidstr), uuidstr);
}
}
{ }
};
+static const struct {
+ uint8_t bit;
+ const char *str;
+} mesh_oob_table[] = {
+ { 0, "Other" },
+ { 1, "Electronic / URI" },
+ { 2, "2D machine-readable code" },
+ { 3, "Bar code" },
+ { 4, "Near Field Communication (NFC)" },
+ { 5, "Number" },
+ { 6, "String" },
+ { 11, "On box" },
+ { 12, "Inside box" },
+ { 13, "On piece of paper" },
+ { 14, "Inside manual" },
+ { 15, "On device" },
+ { }
+};
+
+static void print_mesh_beacon(const uint8_t *data, uint8_t len)
+{
+ uint16_t oob;
+ int i;
+
+ print_hex_field("Mesh Beacon", data, len);
+
+ if (len < 1)
+ return;
+
+ switch (data[0]) {
+ case 0x00:
+ print_field(" Unprovisioned Device Beacon (0x00)");
+ if (len < 18) {
+ packet_hexdump(data + 1, len - 1);
+ break;
+ }
+
+ print_hex_field(" Device UUID", data + 1, 16);
+
+ oob = get_be16(data + 17);
+ print_field(" OOB Information: 0x%4.4x", oob);
+
+ for (i = 0; mesh_oob_table[i].str; i++) {
+ if (oob & (1 << mesh_oob_table[i].bit))
+ print_field(" %s", mesh_oob_table[i].str);
+ }
+
+ if (len < 23) {
+ packet_hexdump(data + 18, len - 18);
+ break;
+ }
+
+ print_field(" URI Hash: 0x%8.8x", get_be32(data + 19));
+ packet_hexdump(data + 23, len - 23);
+ break;
+ case 0x01:
+ print_field(" Secure Network Beacon (0x01)");
+ if (len < 22) {
+ packet_hexdump(data + 1, len - 1);
+ break;
+ }
+
+ print_field(" Flags: 0x%2.2x", data[0]);
+
+ if (data[1] & 0x01)
+ print_field(" Key Refresh");
+
+ if (data[1] & 0x02)
+ print_field(" IV Update");
+
+ print_hex_field(" Network Id", data + 2, 8);
+ print_field(" IV Index: 0x%08x", get_be32(data + 10));
+ print_hex_field(" Authentication Value", data + 14, 8);
+ packet_hexdump(data + 22, len - 22);
+ break;
+ default:
+ print_field(" Invalid Beacon (0x%02x)", data[0]);
+ packet_hexdump(data, len);
+ break;
+ }
+}
+
+static void print_mesh_prov(const uint8_t *data, uint8_t len)
+{
+ print_hex_field("Mesh Provisioning", data, len);
+
+ if (len < 6) {
+ packet_hexdump(data, len);
+ return;
+ }
+
+ print_field(" Link ID: 0x%08x", get_be32(data));
+ print_field(" Transaction Number: %u", data[4]);
+
+ data += 5;
+ len -= 5;
+
+ switch (data[0] & 0x03) {
+ case 0x00:
+ print_field(" Transaction Start (0x00)");
+ if (len < 5) {
+ packet_hexdump(data + 1, len - 1);
+ return;
+ }
+ print_field(" SeqN: %u", data[0] & 0xfc >> 2);
+ print_field(" TotalLength: %u", get_be16(data + 1));
+ print_field(" FCS: 0x%2.2x", data[3]);
+ print_hex_field(" Data", data + 4, len - 4);
+ packet_hexdump(data + 5, len - 5);
+ break;
+ case 0x01:
+ print_field(" Transaction Acknowledgment (0x01)");
+ packet_hexdump(data + 1, len - 1);
+ break;
+ case 0x02:
+ print_field(" Transaction Continuation (0x02)");
+ print_field(" SegmentIndex: %u", data[0] >> 2);
+ if (len < 2) {
+ packet_hexdump(data + 1, len - 1);
+ return;
+ }
+ print_hex_field(" Data", data + 1, len - 1);
+ packet_hexdump(data + 2, len - 2);
+ break;
+ case 0x03:
+ print_field(" Provisioning Bearer Control (0x03)");
+ switch (data[0] >> 2) {
+ case 0x00:
+ print_field(" Link Open (0x00)");
+ if (len < 17) {
+ packet_hexdump(data + 1, len - 1);
+ break;
+ }
+ print_hex_field(" Device UUID", data, 16);
+ break;
+ case 0x01:
+ print_field(" Link Ack (0x01)");
+ break;
+ case 0x02:
+ print_field(" Link Close (0x02)");
+ if (len < 2) {
+ packet_hexdump(data + 1, len - 1);
+ break;
+ }
+
+ switch (data[1]) {
+ case 0x00:
+ print_field(" Reason: Success (0x00)");
+ break;
+ case 0x01:
+ print_field(" Reason: Timeout (0x01)");
+ break;
+ case 0x02:
+ print_field(" Reason: Fail (0x02)");
+ break;
+ default:
+ print_field(" Reason: Unrecognized (0x%2.2x)",
+ data[1]);
+ }
+ packet_hexdump(data + 2, len - 2);
+ break;
+ default:
+ packet_hexdump(data + 1, len - 1);
+ break;
+ }
+ break;
+ default:
+ print_field(" Invalid Command (0x%02x)", data[0]);
+ packet_hexdump(data, len);
+ break;
+ }
+}
+
+static void print_mesh_data(const uint8_t *data, uint8_t len)
+{
+ print_hex_field("Mesh Data", data, len);
+
+ if (len < 1)
+ return;
+
+ print_field(" IVI: %u", data[0] >> 7);
+ print_field(" NID: 0x%2.2x", data[0] & 0x7f);
+ packet_hexdump(data + 1, len - 1);
+}
+
static void print_eir(const uint8_t *eir, uint8_t eir_len, bool le)
{
uint16_t len = 0;
print_field(" Path Loss Threshold: %d", data[1]);
break;
+ case BT_EIR_MESH_DATA:
+ print_mesh_data(data, data_len);
+ break;
+
+ case BT_EIR_MESH_PROV:
+ print_mesh_prov(data, data_len);
+ break;
+
+ case BT_EIR_MESH_BEACON:
+ print_mesh_beacon(data, data_len);
+ break;
+
case BT_EIR_MANUFACTURER_DATA:
if (data_len < 2)
break;
addr[5], addr[4], addr[3], addr[2], addr[1], addr[0]);
}
-#define MAX_INDEX 16
-
-struct index_data {
- uint8_t type;
- uint8_t bdaddr[6];
- uint16_t manufacturer;
-};
-
-static struct index_data index_list[MAX_INDEX];
-
void packet_monitor(struct timeval *tv, struct ucred *cred,
uint16_t index, uint16_t opcode,
const void *data, uint16_t size)
uint16_t manufacturer;
const char *ident;
- if (index_filter && index_number != index)
- return;
-
- index_current = index;
+ if (index != HCI_DEV_NONE) {
+ if (index_filter && index_number != index)
+ return;
+ index_current = index;
+ }
if (tv && time_offset == ((time_t) -1))
time_offset = tv->tv_sec;
const struct bt_hci_cmd_set_conn_encrypt *cmd = data;
print_handle(cmd->handle);
- print_encr_mode(cmd->encr_mode);
+ print_enable("Encryption", cmd->encr_mode);
}
static void change_conn_link_key_cmd(const void *data, uint8_t size)
const struct bt_hci_rsp_read_sync_flow_control *rsp = data;
print_status(rsp->status);
- print_sync_flow_control(rsp->enable);
+ print_enable("Flow control", rsp->enable);
}
static void write_sync_flow_control_cmd(const void *data, uint8_t size)
{
const struct bt_hci_cmd_write_sync_flow_control *cmd = data;
- print_sync_flow_control(cmd->enable);
+ print_enable("Flow control", cmd->enable);
}
static void set_host_flow_control_cmd(const void *data, uint8_t size)
const struct bt_hci_rsp_read_afh_assessment_mode *rsp = data;
print_status(rsp->status);
- print_afh_mode(rsp->mode);
+ print_enable("Mode", rsp->mode);
}
static void write_afh_assessment_mode_cmd(const void *data, uint8_t size)
{
const struct bt_hci_cmd_write_afh_assessment_mode *cmd = data;
- print_afh_mode(cmd->mode);
+ print_enable("Mode", cmd->mode);
}
static void read_ext_inquiry_response_rsp(const void *data, uint8_t size)
const struct bt_hci_rsp_read_simple_pairing_mode *rsp = data;
print_status(rsp->status);
- print_simple_pairing_mode(rsp->mode);
+ print_enable("Mode", rsp->mode);
}
static void write_simple_pairing_mode_cmd(const void *data, uint8_t size)
{
const struct bt_hci_cmd_write_simple_pairing_mode *cmd = data;
- print_simple_pairing_mode(cmd->mode);
+ print_enable("Mode", cmd->mode);
}
static void read_local_oob_data_rsp(const void *data, uint8_t size)
const struct bt_hci_rsp_read_erroneous_reporting *rsp = data;
print_status(rsp->status);
- print_erroneous_reporting(rsp->mode);
+ print_enable("Mode", rsp->mode);
}
static void write_erroneous_reporting_cmd(const void *data, uint8_t size)
{
const struct bt_hci_cmd_write_erroneous_reporting *cmd = data;
- print_erroneous_reporting(cmd->mode);
+ print_enable("Mode", cmd->mode);
}
static void enhanced_flush_cmd(const void *data, uint8_t size)
const struct bt_hci_cmd_short_range_mode *cmd = data;
print_phy_handle(cmd->phy_handle);
- print_short_range_mode(cmd->mode);
+ print_enable("Short range mode", cmd->mode);
}
static void read_le_host_supported_rsp(const void *data, uint8_t size)
const struct bt_hci_rsp_read_secure_conn_support *rsp = data;
print_status(rsp->status);
- print_secure_conn_support(rsp->support);
+ print_enable("Support", rsp->support);
}
static void write_secure_conn_support_cmd(const void *data, uint8_t size)
{
const struct bt_hci_cmd_write_secure_conn_support *cmd = data;
- print_secure_conn_support(cmd->support);
+ print_enable("Support", cmd->support);
}
static void read_auth_payload_timeout_cmd(const void *data, uint8_t size)
print_status(rsp->status);
print_handle(rsp->handle);
- print_afh_mode(rsp->mode);
+ print_enable("Mode", rsp->mode);
print_channel_map(rsp->map);
}
static void set_triggered_clock_capture_cmd(const void *data, uint8_t size)
{
const struct bt_hci_cmd_set_triggered_clock_capture *cmd = data;
- const char *str;
print_handle(cmd->handle);
-
- switch (cmd->enable) {
- case 0x00:
- str = "Disabled";
- break;
- case 0x01:
- str = "Enabled";
- break;
- default:
- str = "Reserved";
- break;
- }
-
- print_field("Capture: %s (0x%2.2x)", str, cmd->enable);
-
+ print_enable("Capture", cmd->enable);
print_clock_type(cmd->type);
print_lpo_allowed(cmd->lpo_allowed);
print_field("Clock captures to filter: %u", cmd->num_filter);
{
const struct bt_hci_cmd_write_ssp_debug_mode *cmd = data;
- print_ssp_debug_mode(cmd->mode);
+ print_enable("Debug Mode", cmd->mode);
}
static void le_set_event_mask_cmd(const void *data, uint8_t size)
print_own_addr_type(cmd->own_addr_type);
print_addr_type("Direct address type", cmd->direct_addr_type);
print_addr("Direct address", cmd->direct_addr, cmd->direct_addr_type);
-
- switch (cmd->channel_map) {
- case 0x01:
- str = "37";
- break;
- case 0x02:
- str = "38";
- break;
- case 0x03:
- str = "37, 38";
- break;
- case 0x04:
- str = "39";
- break;
- case 0x05:
- str = "37, 39";
- break;
- case 0x06:
- str = "38, 39";
- break;
- case 0x07:
- str = "37, 38, 39";
- break;
- default:
- str = "Reserved";
- break;
- }
-
- print_field("Channel map: %s (0x%2.2x)", str, cmd->channel_map);
-
- switch (cmd->filter_policy) {
- case 0x00:
- str = "Allow Scan Request from Any, "
- "Allow Connect Request from Any";
- break;
- case 0x01:
- str = "Allow Scan Request from White List Only, "
- "Allow Connect Request from Any";
- break;
- case 0x02:
- str = "Allow Scan Request from Any, "
- "Allow Connect Request from White List Only";
- break;
- case 0x03:
- str = "Allow Scan Request from White List Only, "
- "Allow Connect Request from White List Only";
- break;
- default:
- str = "Reserved";
- break;
- }
-
- print_field("Filter policy: %s (0x%2.2x)", str, cmd->filter_policy);
+ print_adv_channel_map("Channel map", cmd->channel_map);
+ print_adv_filter_policy("Filter policy", cmd->filter_policy);
}
static void le_read_adv_tx_power_rsp(const void *data, uint8_t size)
static void le_set_adv_enable_cmd(const void *data, uint8_t size)
{
const struct bt_hci_cmd_le_set_adv_enable *cmd = data;
- const char *str;
-
- switch (cmd->enable) {
- case 0x00:
- str = "Disabled";
- break;
- case 0x01:
- str = "Enabled";
- break;
- default:
- str = "Reserved";
- break;
- }
- print_field("Advertising: %s (0x%2.2x)", str, cmd->enable);
+ print_enable("Advertising", cmd->enable);
}
-static void le_set_scan_parameters_cmd(const void *data, uint8_t size)
+static void print_scan_type(const char *label, uint8_t type)
{
- const struct bt_hci_cmd_le_set_scan_parameters *cmd = data;
const char *str;
- switch (cmd->type) {
+ switch (type) {
case 0x00:
str = "Passive";
break;
break;
}
- print_field("Type: %s (0x%2.2x)", str, cmd->type);
+ print_field("%s: %s (0x%2.2x)", label, str, type);
+}
- print_interval(cmd->interval);
- print_window(cmd->window);
- print_own_addr_type(cmd->own_addr_type);
+static void print_scan_filter_policy(uint8_t policy)
+{
+ const char *str;
- switch (cmd->filter_policy) {
+ switch (policy) {
case 0x00:
str = "Accept all advertisement";
break;
case 0x01:
str = "Ignore not in white list";
break;
+ case 0x02:
+ str = "Accept all advertisement, inc. directed unresolved RPA";
+ break;
+ case 0x03:
+ str = "Ignore not in white list, exc. directed unresolved RPA";
+ break;
default:
str = "Reserved";
break;
}
- print_field("Filter policy: %s (0x%2.2x)", str, cmd->filter_policy);
+ print_field("Filter policy: %s (0x%2.2x)", str, policy);
}
-static void le_set_scan_enable_cmd(const void *data, uint8_t size)
+static void le_set_scan_parameters_cmd(const void *data, uint8_t size)
{
- const struct bt_hci_cmd_le_set_scan_enable *cmd = data;
- const char *str;
-
- switch (cmd->enable) {
- case 0x00:
- str = "Disabled";
- break;
- case 0x01:
- str = "Enabled";
- break;
- default:
- str = "Reserved";
- break;
- }
+ const struct bt_hci_cmd_le_set_scan_parameters *cmd = data;
- print_field("Scanning: %s (0x%2.2x)", str, cmd->enable);
+ print_scan_type("Type", cmd->type);
+ print_interval(cmd->interval);
+ print_window(cmd->window);
+ print_own_addr_type(cmd->own_addr_type);
+ print_scan_filter_policy(cmd->filter_policy);
+}
- switch (cmd->filter_dup) {
- case 0x00:
- str = "Disabled";
- break;
- case 0x01:
- str = "Enabled";
- break;
- default:
- str = "Reserved";
- break;
- }
+static void le_set_scan_enable_cmd(const void *data, uint8_t size)
+{
+ const struct bt_hci_cmd_le_set_scan_enable *cmd = data;
- print_field("Filter duplicates: %s (0x%2.2x)", str, cmd->filter_dup);
+ print_enable("Scanning", cmd->enable);
+ print_enable("Filter duplicates", cmd->filter_dup);
}
static void le_create_conn_cmd(const void *data, uint8_t size)
print_slot_125("Min connection interval", cmd->min_interval);
print_slot_125("Max connection interval", cmd->max_interval);
- print_field("Connection latency: 0x%4.4x", le16_to_cpu(cmd->latency));
+ print_conn_latency("Connection latency", cmd->latency);
print_field("Supervision timeout: %d msec (0x%4.4x)",
le16_to_cpu(cmd->supv_timeout) * 10,
le16_to_cpu(cmd->supv_timeout));
print_handle(cmd->handle);
print_slot_125("Min connection interval", cmd->min_interval);
print_slot_125("Max connection interval", cmd->max_interval);
- print_field("Connection latency: 0x%4.4x", le16_to_cpu(cmd->latency));
+ print_conn_latency("Connection latency", cmd->latency);
print_field("Supervision timeout: %d msec (0x%4.4x)",
le16_to_cpu(cmd->supv_timeout) * 10,
le16_to_cpu(cmd->supv_timeout));
print_handle(cmd->handle);
print_slot_125("Min connection interval", cmd->min_interval);
print_slot_125("Max connection interval", cmd->max_interval);
- print_field("Connection latency: 0x%4.4x", le16_to_cpu(cmd->latency));
+ print_conn_latency("Connection latency", cmd->latency);
print_field("Supervision timeout: %d msec (0x%4.4x)",
le16_to_cpu(cmd->supv_timeout) * 10,
le16_to_cpu(cmd->supv_timeout));
static void le_set_resolv_enable_cmd(const void *data, uint8_t size)
{
const struct bt_hci_cmd_le_set_resolv_enable *cmd = data;
- const char *str;
-
- switch (cmd->enable) {
- case 0x00:
- str = "Disabled";
- break;
- case 0x01:
- str = "Enabled";
- break;
- default:
- str = "Reserved";
- break;
- }
- print_field("Address resolution: %s (0x%2.2x)", str, cmd->enable);
+ print_enable("Address resolution", cmd->enable);
}
static void le_set_resolv_timeout_cmd(const void *data, uint8_t size)
print_field("Max RX time: %d", le16_to_cpu(rsp->max_rx_time));
}
-struct opcode_data {
- uint16_t opcode;
- int bit;
+static void le_read_phy_cmd(const void *data, uint8_t size)
+{
+ const struct bt_hci_cmd_le_read_phy *cmd = data;
+
+ print_handle(cmd->handle);
+}
+
+static void print_le_phy(const char *prefix, uint8_t phy)
+{
const char *str;
- void (*cmd_func) (const void *data, uint8_t size);
- uint8_t cmd_size;
+
+ switch (phy) {
+ case 0x01:
+ str = "LE 1M";
+ break;
+ case 0x02:
+ str = "LE 2M";
+ break;
+ case 0x03:
+ str = "LE Coded";
+ break;
+ default:
+ str = "Reserved";
+ break;
+ }
+
+ print_field("%s: %s (0x%2.2x)", prefix, str, phy);
+}
+
+static void le_read_phy_rsp(const void *data, uint8_t size)
+{
+ const struct bt_hci_rsp_le_read_phy *rsp = data;
+
+ print_status(rsp->status);
+ print_handle(rsp->handle);
+ print_le_phy("TX PHY", rsp->tx_phy);
+ print_le_phy("RX PHY", rsp->rx_phy);
+}
+
+static const struct {
+ uint8_t bit;
+ const char *str;
+} le_phys[] = {
+ { 0, "LE 1M" },
+ { 1, "LE 2M" },
+ { 2, "LE Coded"},
+ { }
+};
+
+static const struct {
+ uint8_t bit;
+ const char *str;
+} le_phy_preference[] = {
+ { 0, "No TX PHY preference" },
+ { 1, "No RX PHY preference" },
+ { }
+};
+
+static void print_le_phys_preference(uint8_t all_phys, uint8_t tx_phys,
+ uint8_t rx_phys)
+{
+ int i;
+ uint8_t mask = all_phys;
+
+ print_field("All PHYs preference: 0x%2.2x", all_phys);
+
+ for (i = 0; le_phy_preference[i].str; i++) {
+ if (all_phys & (((uint8_t) 1) << le_phy_preference[i].bit)) {
+ print_field(" %s", le_phy_preference[i].str);
+ mask &= ~(((uint64_t) 1) << le_phy_preference[i].bit);
+ }
+ }
+
+ if (mask)
+ print_text(COLOR_UNKNOWN_OPTIONS_BIT, " Reserved"
+ " (0x%2.2x)", mask);
+
+ print_field("TX PHYs preference: 0x%2.2x", tx_phys);
+ mask = tx_phys;
+
+ for (i = 0; le_phys[i].str; i++) {
+ if (tx_phys & (((uint8_t) 1) << le_phys[i].bit)) {
+ print_field(" %s", le_phys[i].str);
+ mask &= ~(((uint64_t) 1) << le_phys[i].bit);
+ }
+ }
+
+ if (mask)
+ print_text(COLOR_UNKNOWN_OPTIONS_BIT, " Reserved"
+ " (0x%2.2x)", mask);
+
+ print_field("RX PHYs preference: 0x%2.2x", rx_phys);
+ mask = rx_phys;
+
+ for (i = 0; le_phys[i].str; i++) {
+ if (rx_phys & (((uint8_t) 1) << le_phys[i].bit)) {
+ print_field(" %s", le_phys[i].str);
+ mask &= ~(((uint64_t) 1) << le_phys[i].bit);
+ }
+ }
+
+ if (mask)
+ print_text(COLOR_UNKNOWN_OPTIONS_BIT, " Reserved"
+ " (0x%2.2x)", mask);
+}
+
+static void le_set_default_phy_cmd(const void *data, uint8_t size)
+{
+ const struct bt_hci_cmd_le_set_default_phy *cmd = data;
+
+ print_le_phys_preference(cmd->all_phys, cmd->tx_phys, cmd->rx_phys);
+}
+
+static void le_set_phy_cmd(const void *data, uint8_t size)
+{
+ const struct bt_hci_cmd_le_set_phy *cmd = data;
+ const char *str;
+
+ print_handle(cmd->handle);
+ print_le_phys_preference(cmd->all_phys, cmd->tx_phys, cmd->rx_phys);
+ switch (le16_to_cpu(cmd->phy_opts)) {
+ case 0x0001:
+ str = "S2 coding";
+ break;
+ case 0x0002:
+ str = "S8 coding";
+ break;
+ default:
+ str = "Reserved";
+ break;
+ }
+
+ print_field("PHY options preference: %s (0x%4.4x)", str, cmd->phy_opts);
+}
+
+static void le_enhanced_receiver_test_cmd(const void *data, uint8_t size)
+{
+ const struct bt_hci_cmd_le_enhanced_receiver_test *cmd = data;
+ const char *str;
+
+ print_field("RX channel frequency: %d MHz (0x%2.2x)",
+ (cmd->rx_channel * 2) + 2402, cmd->rx_channel);
+ print_le_phy("PHY", cmd->phy);
+
+ switch (cmd->modulation_index) {
+ case 0x00:
+ str = "Standard";
+ break;
+ case 0x01:
+ str = "Stable";
+ break;
+ default:
+ str = "Reserved";
+ break;
+ }
+
+ print_field("Modulation index: %s (0x%2.2x)", str,
+ cmd->modulation_index);
+}
+
+static void le_enhanced_transmitter_test_cmd(const void *data, uint8_t size)
+{
+ const struct bt_hci_cmd_le_enhanced_transmitter_test *cmd = data;
+ const char *str;
+
+ print_field("TX channel frequency: %d MHz (0x%2.2x)",
+ (cmd->tx_channel * 2) + 2402, cmd->tx_channel);
+ print_field("Test data length: %d bytes", cmd->data_len);
+ print_field("Packet payload: 0x%2.2x", cmd->payload);
+
+ switch (cmd->phy) {
+ case 0x01:
+ str = "LE 1M";
+ break;
+ case 0x02:
+ str = "LE 2M";
+ break;
+ case 0x03:
+ str = "LE Coded with S=8";
+ break;
+ case 0x04:
+ str = "LE Coded with S=2";
+ break;
+ default:
+ str = "Reserved";
+ break;
+ }
+
+ print_field("PHY: %s (0x%2.2x)", str, cmd->phy);
+}
+
+static void le_set_adv_set_rand_addr(const void *data, uint8_t size)
+{
+ const struct bt_hci_cmd_le_set_adv_set_rand_addr *cmd = data;
+
+ print_field("Advertising handle: 0x%2.2x", cmd->handle);
+ print_addr("Advertising random address", cmd->bdaddr, 0x00);
+}
+
+static const struct {
+ uint8_t bit;
+ const char *str;
+} ext_adv_properties_table[] = {
+ { 0, "Connectable" },
+ { 1, "Scannable" },
+ { 2, "Directed" },
+ { 3, "High Duty Cycle Directed Connectable" },
+ { 4, "Use legacy advertising PDUs" },
+ { 5, "Anonymous advertising" },
+ { 6, "Include TxPower" },
+ { }
+};
+
+static const char *get_adv_pdu_desc(uint16_t flags)
+{
+ const char *str;
+
+ switch (flags) {
+ case 0x10:
+ str = "ADV_NONCONN_IND";
+ break;
+ case 0x12:
+ str = "ADV_SCAN_IND";
+ break;
+ case 0x13:
+ str = "ADV_IND";
+ break;
+ case 0x15:
+ str = "ADV_DIRECT_IND (low duty cycle)";
+ break;
+ case 0x1d:
+ str = "ADV_DIRECT_IND (high duty cycle)";
+ break;
+ default:
+ str = "Reserved";
+ break;
+ }
+
+ return str;
+}
+
+static void print_ext_adv_properties(uint16_t flags)
+{
+ uint16_t mask = flags;
+ const char *property;
+ int i;
+
+ print_field("Properties: 0x%4.4x", flags);
+
+ for (i = 0; ext_adv_properties_table[i].str; i++) {
+ if (flags & (1 << ext_adv_properties_table[i].bit)) {
+ property = ext_adv_properties_table[i].str;
+
+ if (ext_adv_properties_table[i].bit == 4) {
+ print_field(" %s: %s", property,
+ get_adv_pdu_desc(flags));
+ } else {
+ print_field(" %s", property);
+ }
+ mask &= ~(1 << ext_adv_properties_table[i].bit);
+ }
+ }
+
+ if (mask)
+ print_text(COLOR_UNKNOWN_ADV_FLAG,
+ " Unknown advertising properties (0x%4.4x)",
+ mask);
+}
+
+static void print_ext_slot_625(const char *label, const uint8_t value[3])
+{
+ uint32_t value_cpu = value[0];
+
+ value_cpu |= value[1] << 8;
+ value_cpu |= value[2] << 16;
+
+ print_field("%s: %.3f msec (0x%4.4x)", label,
+ value_cpu * 0.625, value_cpu);
+}
+
+static void le_set_ext_adv_params_cmd(const void *data, uint8_t size)
+{
+ const struct bt_hci_cmd_le_set_ext_adv_params *cmd = data;
+ const char *str;
+
+ print_field("Handle: 0x%2.2x", cmd->handle);
+ print_ext_adv_properties(le16_to_cpu(cmd->evt_properties));
+
+ print_ext_slot_625("Min advertising interval", cmd->min_interval);
+ print_ext_slot_625("Max advertising interval", cmd->max_interval);
+ print_adv_channel_map("Channel map", cmd->channel_map);
+ print_own_addr_type(cmd->own_addr_type);
+ print_peer_addr_type("Peer address type", cmd->peer_addr_type);
+ print_addr("Peer address", cmd->peer_addr, cmd->peer_addr_type);
+ print_adv_filter_policy("Filter policy", cmd->filter_policy);
+ print_power_level(cmd->tx_power, NULL);
+
+ switch (cmd->primary_phy) {
+ case 0x01:
+ str = "LE 1M";
+ break;
+ case 0x03:
+ str = "LE Coded";
+ break;
+ default:
+ str = "Reserved";
+ break;
+ }
+
+ print_field("Primary PHY: %s (0x%2.2x)", str, cmd->primary_phy);
+ print_field("Secondary max skip: 0x%2.2x", cmd->secondary_max_skip);
+ print_le_phy("Secondary PHY", cmd->secondary_phy);
+ print_field("SID: 0x%2.2x", cmd->sid);
+ print_enable("Scan request notifications", cmd->notif_enable);
+}
+
+static void le_set_ext_adv_params_rsp(const void *data, uint8_t size)
+{
+ const struct bt_hci_rsp_le_set_ext_adv_params *rsp = data;
+
+ print_status(rsp->status);
+ print_power_level(rsp->tx_power, "selected");
+}
+
+static void le_set_ext_adv_data_cmd(const void *data, uint8_t size)
+{
+ const struct bt_hci_cmd_le_set_ext_adv_data *cmd = data;
+ const char *str;
+
+ print_field("Handle: 0x%2.2x", cmd->handle);
+
+ switch (cmd->operation) {
+ case 0x00:
+ str = "Immediate fragment";
+ break;
+ case 0x01:
+ str = "First fragment";
+ break;
+ case 0x02:
+ str = "Last fragment";
+ break;
+ case 0x03:
+ str = "Complete extended advertising data";
+ break;
+ case 0x04:
+ str = "Unchanged data";
+ break;
+ default:
+ str = "Reserved";
+ break;
+ }
+
+ print_field("Operation: %s (0x%2.2x)", str, cmd->operation);
+
+ switch (cmd->fragment_preference) {
+ case 0x00:
+ str = "Fragment all";
+ break;
+ case 0x01:
+ str = "Minimize fragmentation";
+ break;
+ default:
+ str = "Reserved";
+ break;
+ }
+
+ print_field("Fragment preference: %s (0x%2.2x)", str,
+ cmd->fragment_preference);
+ print_field("Data length: 0x%2.2x", cmd->data_len);
+ packet_print_ad(cmd->data, size - sizeof(*cmd));
+}
+
+static void le_set_ext_scan_rsp_data_cmd(const void *data, uint8_t size)
+{
+ const struct bt_hci_cmd_le_set_ext_scan_rsp_data *cmd = data;
+ const char *str;
+
+ print_field("Handle: 0x%2.2x", cmd->handle);
+
+ switch (cmd->operation) {
+ case 0x00:
+ str = "Immediate fragment";
+ break;
+ case 0x01:
+ str = "First fragment";
+ break;
+ case 0x02:
+ str = "Last fragment";
+ break;
+ case 0x03:
+ str = "Complete scan response data";
+ break;
+ case 0x04:
+ str = "Unchanged data";
+ break;
+ default:
+ str = "Reserved";
+ break;
+ }
+
+ print_field("Operation: %s (0x%2.2x)", str, cmd->operation);
+
+ switch (cmd->fragment_preference) {
+ case 0x00:
+ str = "Fragment all";
+ break;
+ case 0x01:
+ str = "Minimize fragmentation";
+ break;
+ default:
+ str = "Reserved";
+ break;
+ }
+
+ print_field("Fragment preference: %s (0x%2.2x)", str,
+ cmd->fragment_preference);
+ print_field("Data length: 0x%2.2x", cmd->data_len);
+ packet_print_ad(cmd->data, size - sizeof(*cmd));
+}
+
+static void le_set_ext_adv_enable_cmd(const void *data, uint8_t size)
+{
+ const struct bt_hci_cmd_le_set_ext_adv_enable *cmd = data;
+ const struct bt_hci_cmd_ext_adv_set *adv_set;
+ int i;
+
+ print_enable("Extended advertising", cmd->enable);
+
+ if (cmd->num_of_sets == 0)
+ print_field("Number of sets: Disable all sets (0x%2.2x)",
+ cmd->num_of_sets);
+ else if (cmd->num_of_sets > 0x3f)
+ print_field("Number of sets: Reserved (0x%2.2x)",
+ cmd->num_of_sets);
+ else
+ print_field("Number of sets: %u (0x%2.2x)", cmd->num_of_sets,
+ cmd->num_of_sets);
+
+ for (i = 0; i < cmd->num_of_sets; ++i) {
+ adv_set = data + 2 + i * sizeof(struct bt_hci_cmd_ext_adv_set);
+ print_field("Entry %d", i);
+ print_field(" Handle: 0x%2.2x", adv_set->handle);
+ print_field(" Duration: %d ms (0x%2.2x)",
+ adv_set->duration * 10, adv_set->duration);
+ print_field(" Max ext adv events: %d", adv_set->max_events);
+ }
+}
+
+static void le_read_max_adv_data_len_rsp(const void *data, uint8_t size)
+{
+ const struct bt_hci_rsp_le_read_max_adv_data_len *rsp = data;
+
+ print_status(rsp->status);
+ print_field("Max length: %d", rsp->max_len);
+}
+
+static void le_read_num_supported_adv_sets_rsp(const void *data, uint8_t size)
+{
+ const struct bt_hci_rsp_le_read_num_supported_adv_sets *rsp = data;
+
+ print_status(rsp->status);
+ print_field("Num supported adv sets: %d", rsp->num_of_sets);
+}
+
+static void le_remove_adv_set_cmd(const void *data, uint8_t size)
+{
+ const struct bt_hci_cmd_le_remove_adv_set *cmd = data;
+
+ print_handle(cmd->handle);
+}
+
+static const struct {
+ uint8_t bit;
+ const char *str;
+} periodic_adv_properties_table[] = {
+ { 6, "Include TxPower" },
+ { }
+};
+
+static void print_periodic_adv_properties(uint16_t flags)
+{
+ uint16_t mask = flags;
+ int i;
+
+ print_field("Properties: 0x%4.4x", flags);
+
+ for (i = 0; periodic_adv_properties_table[i].str; i++) {
+ if (flags & (1 << periodic_adv_properties_table[i].bit)) {
+ print_field(" %s",
+ periodic_adv_properties_table[i].str);
+ mask &= ~(1 << periodic_adv_properties_table[i].bit);
+ }
+ }
+
+ if (mask)
+ print_text(COLOR_UNKNOWN_ADV_FLAG,
+ " Unknown advertising properties (0x%4.4x)",
+ mask);
+}
+
+static void le_set_periodic_adv_params_cmd(const void *data, uint8_t size)
+{
+ const struct bt_hci_cmd_le_set_periodic_adv_params *cmd = data;
+
+ print_handle(cmd->handle);
+ print_slot_125("Min interval", cmd->min_interval);
+ print_slot_125("Max interval", cmd->max_interval);
+ print_periodic_adv_properties(cmd->properties);
+}
+
+static void le_set_periodic_adv_data_cmd(const void *data, uint8_t size)
+{
+ const struct bt_hci_cmd_le_set_periodic_adv_data *cmd = data;
+ const char *str;
+
+ print_handle(cmd->handle);
+
+ switch (cmd->operation) {
+ case 0x00:
+ str = "Immediate fragment";
+ break;
+ case 0x01:
+ str = "First fragment";
+ break;
+ case 0x02:
+ str = "Last fragment";
+ break;
+ case 0x03:
+ str = "Complete ext advertising data";
+ break;
+ default:
+ str = "Reserved";
+ break;
+ }
+
+ print_field("Operation: %s (0x%2.2x)", str, cmd->operation);
+ print_field("Data length: 0x%2.2x", cmd->data_len);
+ print_eir(cmd->data, cmd->data_len, true);
+}
+
+static void le_set_periodic_adv_enable_cmd(const void *data, uint8_t size)
+{
+ const struct bt_hci_cmd_le_set_periodic_adv_enable *cmd = data;
+
+ print_enable("Periodic advertising", cmd->enable);
+ print_handle(cmd->handle);
+}
+
+static const struct {
+ uint8_t bit;
+ const char *str;
+} ext_scan_phys_table[] = {
+ { 0, "LE 1M" },
+ { 2, "LE Coded" },
+ { }
+};
+
+static void print_ext_scan_phys(const void *data, uint8_t flags)
+{
+ const struct bt_hci_le_scan_phy *scan_phy;
+ uint8_t mask = flags;
+ int bits_set = 0;
+ int i;
+
+ print_field("PHYs: 0x%2.2x", flags);
+
+ for (i = 0; ext_scan_phys_table[i].str; i++) {
+ if (flags & (1 << ext_scan_phys_table[i].bit)) {
+ scan_phy = data + bits_set * sizeof(*scan_phy);
+ mask &= ~(1 << ext_scan_phys_table[i].bit);
+
+ print_field("Entry %d: %s", bits_set,
+ ext_scan_phys_table[i].str);
+ print_scan_type(" Type", scan_phy->type);
+ print_slot_625(" Interval", scan_phy->interval);
+ print_slot_625(" Window", scan_phy->window);
+
+ ++bits_set;
+ }
+ }
+
+ if (mask)
+ print_text(COLOR_UNKNOWN_ADV_FLAG, " Unknown scanning PHYs"
+ " (0x%2.2x)", mask);
+}
+
+static void le_set_ext_scan_params_cmd(const void *data, uint8_t size)
+{
+ const struct bt_hci_cmd_le_set_ext_scan_params *cmd = data;
+
+ print_own_addr_type(cmd->own_addr_type);
+ print_scan_filter_policy(cmd->filter_policy);
+ print_ext_scan_phys(cmd->data, cmd->num_phys);
+}
+
+static void le_set_ext_scan_enable_cmd(const void *data, uint8_t size)
+{
+ const struct bt_hci_cmd_le_set_ext_scan_enable *cmd = data;
+
+ print_enable("Extended scan", cmd->enable);
+ print_enable("Filter duplicates", cmd->filter_dup);
+
+ print_field("Duration: %d msec (0x%4.4x)",
+ le16_to_cpu(cmd->duration) * 10,
+ le16_to_cpu(cmd->duration));
+ print_field("Period: %.2f sec (0x%4.4x)",
+ le16_to_cpu(cmd->period) * 1.28,
+ le16_to_cpu(cmd->period));
+}
+
+static const struct {
+ uint8_t bit;
+ const char *str;
+} ext_conn_phys_table[] = {
+ { 0, "LE 1M" },
+ { 1, "LE 2M" },
+ { 2, "LE Coded" },
+ { }
+};
+
+static void print_ext_conn_phys(const void *data, uint8_t flags)
+{
+ const struct bt_hci_le_ext_create_conn *entry;
+ uint8_t mask = flags;
+ int bits_set = 0;
+ int i;
+
+ print_field("Initiating PHYs: 0x%2.2x", flags);
+
+ for (i = 0; ext_conn_phys_table[i].str; i++) {
+ if (flags & (1 << ext_conn_phys_table[i].bit)) {
+ entry = data + bits_set * sizeof(*entry);
+ mask &= ~(1 << ext_conn_phys_table[i].bit);
+
+ print_field("Entry %d: %s", bits_set,
+ ext_conn_phys_table[i].str);
+ print_slot_625(" Scan interval", entry->scan_interval);
+ print_slot_625(" Scan window", entry->scan_window);
+ print_slot_125(" Min connection interval",
+ entry->min_interval);
+ print_slot_125(" Max connection interval",
+ entry->max_interval);
+ print_conn_latency(" Connection latency",
+ entry->latency);
+ print_field(" Supervision timeout: %d msec (0x%4.4x)",
+ le16_to_cpu(entry->supv_timeout) * 10,
+ le16_to_cpu(entry->supv_timeout));
+ print_slot_625(" Min connection length",
+ entry->min_length);
+ print_slot_625(" Max connection length",
+ entry->max_length);
+
+ ++bits_set;
+ }
+ }
+
+ if (mask)
+ print_text(COLOR_UNKNOWN_ADV_FLAG, " Unknown scanning PHYs"
+ " (0x%2.2x)", mask);
+}
+
+static void le_ext_create_conn_cmd(const void *data, uint8_t size)
+{
+ const struct bt_hci_cmd_le_ext_create_conn *cmd = data;
+ const char *str;
+
+ switch (cmd->filter_policy) {
+ case 0x00:
+ str = "White list is not used";
+ break;
+ case 0x01:
+ str = "White list is used";
+ break;
+ default:
+ str = "Reserved";
+ break;
+ }
+
+ print_field("Filter policy: %s (0x%2.2x)", str, cmd->filter_policy);
+
+ print_own_addr_type(cmd->own_addr_type);
+ print_peer_addr_type("Peer address type", cmd->peer_addr_type);
+ print_addr("Peer address", cmd->peer_addr, cmd->peer_addr_type);
+ print_ext_conn_phys(cmd->data, cmd->phys);
+}
+
+static void le_periodic_adv_create_sync_cmd(const void *data, uint8_t size)
+{
+ const struct bt_hci_cmd_le_periodic_adv_create_sync *cmd = data;
+ const char *str;
+
+ switch (cmd->filter_policy) {
+ case 0x00:
+ str = "Use specified advertising parameters";
+ break;
+ case 0x01:
+ str = "Use Periodic Advertiser List";
+ break;
+ default:
+ str = "Reserved";
+ break;
+ }
+
+ print_field("Filter policy: %s (0x%2.2x)", str, cmd->filter_policy);
+ print_field("SID: 0x%2.2x", cmd->sid);
+ print_addr_type("Adv address type", cmd->addr_type);
+ print_addr("Adv address", cmd->addr, cmd->addr_type);
+ print_field("Skip: 0x%4.4x", cmd->skip);
+ print_field("Sync timeout: %d msec (0x%4.4x)",
+ le16_to_cpu(cmd->sync_timeout) * 10,
+ le16_to_cpu(cmd->sync_timeout));
+ print_field("Unused: 0x%2.2x", cmd->unused);
+}
+
+static void le_periodic_adv_term_sync_cmd(const void *data, uint8_t size)
+{
+ const struct bt_hci_cmd_le_periodic_adv_term_sync *cmd = data;
+
+ print_field("Sync handle: 0x%4.4x", cmd->sync_handle);
+}
+
+static void le_add_dev_periodic_adv_list_cmd(const void *data, uint8_t size)
+{
+ const struct bt_hci_cmd_le_add_dev_periodic_adv_list *cmd = data;
+
+ print_addr_type("Adv address type", cmd->addr_type);
+ print_addr("Adv address", cmd->addr, cmd->addr_type);
+ print_field("SID: 0x%2.2x", cmd->sid);
+}
+
+static void le_remove_dev_periodic_adv_list_cmd(const void *data, uint8_t size)
+{
+ const struct bt_hci_cmd_le_remove_dev_periodic_adv_list *cmd = data;
+
+ print_addr_type("Adv address type", cmd->addr_type);
+ print_addr("Adv address", cmd->addr, cmd->addr_type);
+ print_field("SID: 0x%2.2x", cmd->sid);
+}
+
+static void le_read_periodic_adv_list_size_rsp(const void *data, uint8_t size)
+{
+ const struct bt_hci_rsp_le_read_dev_periodic_adv_list_size *rsp = data;
+
+ print_status(rsp->status);
+ print_field("List size: 0x%2.2x", rsp->list_size);
+}
+
+static void le_read_tx_power_rsp(const void *data, uint8_t size)
+{
+ const struct bt_hci_rsp_le_read_tx_power *rsp = data;
+
+ print_status(rsp->status);
+ print_field("Min Tx power: %d dBm", rsp->min_tx_power);
+ print_field("Max Tx power: %d dBm", rsp->max_tx_power);
+}
+
+static void le_read_rf_path_comp_rsp(const void *data, uint8_t size)
+{
+ const struct bt_hci_rsp_le_read_rf_path_comp *rsp = data;
+
+ print_status(rsp->status);
+ print_field("RF Tx Path Compensation Value: 0x%4.4x",
+ rsp->rf_tx_path_comp);
+ print_field("RF Rx Path Compensation Value: 0x%4.4x",
+ rsp->rf_rx_path_comp);
+}
+
+static void le_write_rf_path_comp_cmd(const void *data, uint8_t size)
+{
+ const struct bt_hci_cmd_le_write_rf_path_comp *cmd = data;
+
+ print_field("RF Tx Path Compensation Value: 0x%4.4x",
+ cmd->rf_tx_path_comp);
+ print_field("RF Rx Path Compensation Value: 0x%4.4x",
+ cmd->rf_rx_path_comp);
+}
+
+static void le_set_priv_mode_cmd(const void *data, uint8_t size)
+{
+ const struct bt_hci_cmd_le_set_priv_mode *cmd = data;
+ const char *str;
+
+ print_addr_type("Peer Identity address type", cmd->peer_id_addr_type);
+ print_addr("Peer Identity address", cmd->peer_id_addr,
+ cmd->peer_id_addr_type);
+
+ switch (cmd->priv_mode) {
+ case 0x00:
+ str = "Use Network Privacy";
+ break;
+ case 0x01:
+ str = "Use Device Privacy";
+ break;
+ default:
+ str = "Reserved";
+ break;
+ }
+
+ print_field("Privacy Mode: %s (0x%2.2x)", str, cmd->priv_mode);
+}
+
+struct opcode_data {
+ uint16_t opcode;
+ int bit;
+ const char *str;
+ void (*cmd_func) (const void *data, uint8_t size);
+ uint8_t cmd_size;
bool cmd_fixed;
void (*rsp_func) (const void *data, uint8_t size);
uint8_t rsp_size;
{ 0x202f, 283, "LE Read Maximum Data Length",
null_cmd, 0, true,
le_read_max_data_length_rsp, 9, true },
+ { 0x2030, 284, "LE Read PHY",
+ le_read_phy_cmd, 2, true,
+ le_read_phy_rsp, 5, true},
+ { 0x2031, 285, "LE Set Default PHY",
+ le_set_default_phy_cmd, 3, true,
+ status_rsp, 1, true },
+ { 0x2032, 286, "LE Set PHY",
+ le_set_phy_cmd, 7, true},
+ { 0x2033, 287, "LE Enhanced Receiver Test",
+ le_enhanced_receiver_test_cmd, 3, true,
+ status_rsp, 1, true },
+ { 0x2034, 288, "LE Enhanced Transmitter Test",
+ le_enhanced_transmitter_test_cmd, 4, true,
+ status_rsp, 1, true },
+ { 0x2035, 289, "LE Set Advertising Set Random Address",
+ le_set_adv_set_rand_addr, 7, true,
+ status_rsp, 1, true },
+ { 0x2036, 290, "LE Set Extended Advertising Parameters",
+ le_set_ext_adv_params_cmd, 25, true,
+ le_set_ext_adv_params_rsp, 2, true },
+ { 0x2037, 291, "LE Set Extended Advertising Data",
+ le_set_ext_adv_data_cmd, 4, false,
+ status_rsp, 1, true },
+ { 0x2038, 292, "LE Set Extended Scan Response Data",
+ le_set_ext_scan_rsp_data_cmd, 4, false,
+ status_rsp, 1, true },
+ { 0x2039, 293, "LE Set Extended Advertising Enable",
+ le_set_ext_adv_enable_cmd, 2, false,
+ status_rsp, 1, true },
+ { 0x203a, 294, "LE Read Maximum Advertising Data Length",
+ null_cmd, 0, true,
+ le_read_max_adv_data_len_rsp, 3, true },
+ { 0x203b, 295, "LE Read Number of Supported Advertising Sets",
+ null_cmd, 0, true,
+ le_read_num_supported_adv_sets_rsp, 2, true },
+ { 0x203c, 296, "LE Remove Advertising Set",
+ le_remove_adv_set_cmd, 1, true,
+ status_rsp, 1, true },
+ { 0x203d, 297, "LE Clear Advertising Sets",
+ null_cmd, 0, true,
+ status_rsp, 1, true },
+ { 0x203e, 298, "LE Set Periodic Advertising Parameters",
+ le_set_periodic_adv_params_cmd, 7, true,
+ status_rsp, 1, true },
+ { 0x203f, 299, "LE Set Periodic Advertising Data",
+ le_set_periodic_adv_data_cmd, 3, false,
+ status_rsp, 1, true },
+ { 0x2040, 300, "LE Set Periodic Advertising Enable",
+ le_set_periodic_adv_enable_cmd, 2, true,
+ status_rsp, 1, true },
+ { 0x2041, 301, "LE Set Extended Scan Parameters",
+ le_set_ext_scan_params_cmd, 3, false,
+ status_rsp, 1, true },
+ { 0x2042, 302, "LE Set Extended Scan Enable",
+ le_set_ext_scan_enable_cmd, 6, true,
+ status_rsp, 1, true },
+ { 0x2043, 303, "LE Extended Create Connection",
+ le_ext_create_conn_cmd, 10, false,
+ status_rsp, 1, true },
+ { 0x2044, 304, "LE Periodic Advertising Create Sync",
+ le_periodic_adv_create_sync_cmd, 14, true,
+ status_rsp, 1, true },
+ { 0x2045, 305, "LE Periodic Advertising Create Sync Cancel",
+ null_cmd, 0, true,
+ status_rsp, 1, true },
+ { 0x2046, 306, "LE Periodic Advertising Terminate Sync",
+ le_periodic_adv_term_sync_cmd, 2, true,
+ status_rsp, 1, true },
+ { 0x2047, 307, "LE Add Device To Periodic Advertiser List",
+ le_add_dev_periodic_adv_list_cmd, 8, true,
+ status_rsp, 1, true },
+ { 0x2048, 308, "LE Remove Device From Periodic Advertiser List",
+ le_remove_dev_periodic_adv_list_cmd, 8, true,
+ status_rsp, 1, true },
+ { 0x2049, 309, "LE Clear Periodic Advertiser List",
+ null_cmd, 0, true,
+ status_rsp, 1, true },
+ { 0x204a, 310, "LE Read Periodic Advertiser List Size",
+ null_cmd, 0, true,
+ le_read_periodic_adv_list_size_rsp, 2, true },
+ { 0x204b, 311, "LE Read Transmit Power",
+ null_cmd, 0, true,
+ le_read_tx_power_rsp, 3, true },
+ { 0x204c, 312, "LE Read RF Path Compensation",
+ null_cmd, 0, true,
+ le_read_rf_path_comp_rsp, 5, true },
+ { 0x204d, 313, "LE Write RF Path Compensation",
+ le_write_rf_path_comp_cmd, 4, true,
+ status_rsp, 1, true },
+ { 0x204e, 314, "LE Set Privacy Mode",
+ le_set_priv_mode_cmd, 8, true,
+ status_rsp, 1, true },
{ }
};
case 15:
return "Broadcom";
#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
- default:
- return "Unknown";
+ default:
+ return "Unknown";
#endif
}
case 15:
return broadcom_vendor_ocf(ocf);
#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
- default:
- return broadcom_vendor_ocf(ocf);
+ default:
+ return broadcom_vendor_ocf(ocf);
#endif
}
case 15:
return broadcom_vendor_evt(evt);
#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
- default:
- return broadcom_vendor_evt(evt);
+ default:
+ return broadcom_vendor_evt(evt);
#endif
}
print_handle(evt->handle);
print_bdaddr(evt->bdaddr);
print_link_type(evt->link_type);
- print_encr_mode(evt->encr_mode);
+ print_enable("Encryption", evt->encr_mode);
if (evt->status == 0x00)
assign_handle(le16_to_cpu(evt->handle), 0x00);
print_status(evt->status);
print_phy_handle(evt->phy_handle);
- print_short_range_mode(evt->mode);
+ print_enable("Short range mode", evt->mode);
}
static void amp_status_change_evt(const void *data, uint8_t size)
print_peer_addr_type("Peer address type", evt->peer_addr_type);
print_addr("Peer address", evt->peer_addr, evt->peer_addr_type);
print_slot_125("Connection interval", evt->interval);
- print_slot_125("Connection latency", evt->latency);
+ print_conn_latency("Connection latency", evt->latency);
print_field("Supervision timeout: %d msec (0x%4.4x)",
le16_to_cpu(evt->supv_timeout) * 10,
le16_to_cpu(evt->supv_timeout));
print_num_reports(evt->num_reports);
report:
- print_adv_event_type(evt->event_type);
+ print_adv_event_type("Event type", evt->event_type);
print_peer_addr_type("Address type", evt->addr_type);
print_addr("Address", evt->addr, evt->addr_type);
print_field("Data length: %d", evt->data_len);
print_status(evt->status);
print_handle(evt->handle);
print_slot_125("Connection interval", evt->interval);
- print_slot_125("Connection latency", evt->latency);
+ print_conn_latency("Connection latency", evt->latency);
print_field("Supervision timeout: %d msec (0x%4.4x)",
le16_to_cpu(evt->supv_timeout) * 10,
le16_to_cpu(evt->supv_timeout));
print_handle(evt->handle);
print_slot_125("Min connection interval", evt->min_interval);
print_slot_125("Max connection interval", evt->max_interval);
- print_field("Connection latency: 0x%4.4x", le16_to_cpu(evt->latency));
+ print_conn_latency("Connection latency", evt->latency);
print_field("Supervision timeout: %d msec (0x%4.4x)",
le16_to_cpu(evt->supv_timeout) * 10,
le16_to_cpu(evt->supv_timeout));
print_addr("Local resolvable private address", evt->local_rpa, 0x01);
print_addr("Peer resolvable private address", evt->peer_rpa, 0x01);
print_slot_125("Connection interval", evt->interval);
- print_slot_125("Connection latency", evt->latency);
+ print_conn_latency("Connection latency", evt->latency);
print_field("Supervision timeout: %d msec (0x%4.4x)",
le16_to_cpu(evt->supv_timeout) * 10,
le16_to_cpu(evt->supv_timeout));
print_num_reports(evt->num_reports);
- print_adv_event_type(evt->event_type);
+ print_adv_event_type("Event type", evt->event_type);
print_peer_addr_type("Address type", evt->addr_type);
print_addr("Address", evt->addr, evt->addr_type);
print_addr_type("Direct address type", evt->direct_addr_type);
packet_hexdump(data + sizeof(*evt), size - sizeof(*evt));
}
+static void le_phy_update_complete_evt(const void *data, uint8_t size)
+{
+ const struct bt_hci_evt_le_phy_update_complete *evt = data;
+
+ print_status(evt->status);
+ print_handle(evt->handle);
+ print_le_phy("TX PHY", evt->tx_phy);
+ print_le_phy("RX PHY", evt->rx_phy);
+}
+
+static const struct {
+ uint8_t bit;
+ const char *str;
+} ext_adv_report_evt_type[] = {
+ { 0, "Connectable" },
+ { 1, "Scannable" },
+ { 2, "Directed" },
+ { 3, "Scan response" },
+ { 4, "Use legacy advertising PDUs" },
+ { }
+};
+
+static void print_ext_adv_report_evt_type(const char *indent, uint16_t flags)
+{
+ uint16_t mask = flags;
+ uint16_t props = flags;
+ uint8_t data_status;
+ const char *str;
+ int i;
+
+ print_field("%sEvent type: 0x%4.4x", indent, flags);
+
+ props &= 0x1f;
+ print_field("%s Props: 0x%4.4x", indent, props);
+ for (i = 0; ext_adv_report_evt_type[i].str; i++) {
+ if (flags & (1 << ext_adv_report_evt_type[i].bit)) {
+ print_field("%s %s", indent,
+ ext_adv_report_evt_type[i].str);
+ mask &= ~(1 << ext_adv_report_evt_type[i].bit);
+ }
+ }
+
+ data_status = (flags >> 5) & 3;
+ mask &= ~(data_status << 5);
+
+ switch (data_status) {
+ case 0x00:
+ str = "Complete";
+ break;
+ case 0x01:
+ str = "Incomplete, more data to come";
+ break;
+ case 0x02:
+ str = "Incomplete, data truncated, no more to come";
+ break;
+ default:
+ str = "Reserved";
+ break;
+ }
+
+ print_field("%s Data status: %s", indent, str);
+
+ if (mask)
+ print_text(COLOR_UNKNOWN_ADV_FLAG,
+ "%s Reserved (0x%4.4x)", indent, mask);
+}
+
+static void print_legacy_adv_report_pdu(uint16_t flags)
+{
+ const char *str;
+
+ if (!(flags & (1 << 4)))
+ return;
+
+ switch (flags) {
+ case 0x10:
+ str = "ADV_NONCONN_IND";
+ break;
+ case 0x12:
+ str = "ADV_SCAN_IND";
+ break;
+ case 0x13:
+ str = "ADV_IND";
+ break;
+ case 0x15:
+ str = "ADV_DIRECT_IND";
+ break;
+ case 0x1a:
+ str = "SCAN_RSP to an ADV_IND";
+ break;
+ case 0x1b:
+ str = "SCAN_RSP to an ADV_SCAN_IND";
+ break;
+ default:
+ str = "Reserved";
+ break;
+ }
+
+ print_field(" Legacy PDU Type: %s (0x%4.4x)", str, flags);
+}
+
+static void le_ext_adv_report_evt(const void *data, uint8_t size)
+{
+ const struct bt_hci_evt_le_ext_adv_report *evt = data;
+ const struct bt_hci_le_ext_adv_report *report;
+ const char *str;
+ int i;
+
+ print_num_reports(evt->num_reports);
+
+ data += sizeof(evt->num_reports);
+
+ for (i = 0; i < evt->num_reports; ++i) {
+ report = data;
+ print_field("Entry %d", i);
+ print_ext_adv_report_evt_type(" ", report->event_type);
+ print_legacy_adv_report_pdu(report->event_type);
+ print_peer_addr_type(" Address type", report->addr_type);
+ print_addr(" Address", report->addr, report->addr_type);
+
+ switch (report->primary_phy) {
+ case 0x01:
+ str = "LE 1M";
+ break;
+ case 0x03:
+ str = "LE Coded";
+ break;
+ default:
+ str = "Reserved";
+ break;
+ }
+
+ print_field(" Primary PHY: %s", str);
+
+ switch (report->secondary_phy) {
+ case 0x00:
+ str = "No packets";
+ break;
+ case 0x01:
+ str = "LE 1M";
+ break;
+ case 0x02:
+ str = "LE 2M";
+ break;
+ case 0x03:
+ str = "LE Coded";
+ break;
+ default:
+ str = "Reserved";
+ break;
+ }
+
+ print_field(" Secondary PHY: %s", str);
+
+ if (report->sid == 0xff)
+ print_field(" SID: no ADI field (0x%2.2x)",
+ report->sid);
+ else if (report->sid > 0x0f)
+ print_field(" SID: Reserved (0x%2.2x)", report->sid);
+ else
+ print_field(" SID: 0x%2.2x", report->sid);
+
+ print_field(" TX power: %d dBm", report->tx_power);
+
+ if (report->rssi == 127)
+ print_field(" RSSI: not available (0x%2.2x)",
+ (uint8_t) report->rssi);
+ else if (report->rssi >= -127 && report->rssi <= 20)
+ print_field(" RSSI: %d dBm (0x%2.2x)",
+ report->rssi, (uint8_t) report->rssi);
+ else
+ print_field(" RSSI: reserved (0x%2.2x)",
+ (uint8_t) report->rssi);
+
+ print_slot_125(" Periodic advertising invteral",
+ report->interval);
+ print_peer_addr_type(" Direct address type",
+ report->direct_addr_type);
+ print_addr(" Direct address", report->direct_addr,
+ report->direct_addr_type);
+ print_field(" Data length: 0x%2.2x", report->data_len);
+ data += sizeof(struct bt_hci_le_ext_adv_report);
+ packet_hexdump(data, report->data_len);
+ data += report->data_len;
+ }
+}
+
+static void le_adv_set_term_evt(const void *data, uint8_t size)
+{
+ const struct bt_hci_evt_le_adv_set_term *evt = data;
+
+ print_status(evt->status);
+ print_field("Handle: %d", evt->handle);
+ print_field("Connection handle: %d", evt->conn_handle);
+ print_field("Number of completed extended advertising events: %d",
+ evt->num_evts);
+}
+
+static void le_scan_req_received_evt(const void *data, uint8_t size)
+{
+ const struct bt_hci_evt_le_scan_req_received *evt = data;
+
+ print_field("Handle: %d", evt->handle);
+ print_peer_addr_type("Scanner address type", evt->scanner_addr_type);
+ print_addr("Scanner address", evt->scanner_addr,
+ evt->scanner_addr_type);
+}
+
+static void le_chan_select_alg_evt(const void *data, uint8_t size)
+{
+ const struct bt_hci_evt_le_chan_select_alg *evt = data;
+ const char *str;
+
+ print_handle(evt->handle);
+
+ switch (evt->algorithm) {
+ case 0x00:
+ str = "#1";
+ break;
+ case 0x01:
+ str = "#2";
+ break;
+ default:
+ str = "Reserved";
+ break;
+ }
+
+ print_field("Algorithm: %s (0x%2.2x)", str, evt->algorithm);
+}
+
struct subevent_data {
uint8_t subevent;
const char *str;
le_enhanced_conn_complete_evt, 30, true },
{ 0x0b, "LE Direct Advertising Report",
le_direct_adv_report_evt, 1, false },
+ { 0x0c, "LE PHY Update Complete",
+ le_phy_update_complete_evt, 5, true},
+ { 0x0d, "LE Extended Advertising Report",
+ le_ext_adv_report_evt, 1, false},
+ { 0x0e, "LE Periodic Advertising Sync Established" },
+ { 0x0f, "LE Periodic Advertising Report" },
+ { 0x10, "LE Periodic Advertising Sync Lost" },
+ { 0x11, "LE Scan Timeout" },
+ { 0x12, "LE Advertising Set Terminated",
+ le_adv_set_term_evt, 5, true},
+ { 0x13, "LE Scan Request Received",
+ le_scan_req_received_evt, 8, true},
+ { 0x14, "LE Channel Selection Algorithm",
+ le_chan_select_alg_evt, 3, true},
{ }
};
inquiry_response_notify_evt, 4, true },
{ 0x57, "Authenticated Payload Timeout Expired",
auth_payload_timeout_expired_evt, 2, true },
+ { 0x58, "SAM Status Change" },
{ 0xfe, "Testing" },
{ 0xff, "Vendor", vendor_evt, 0, false },
{ }
char extra_str[25], vendor_str[150];
int i;
+ index_list[index].frame++;
+
if (size < HCI_COMMAND_HDR_SIZE) {
sprintf(extra_str, "(len %d)", size);
print_packet(tv, cred, '*', index, NULL, COLOR_ERROR,
char extra_str[25];
int i;
+ index_list[index].frame++;
+
if (size < HCI_EVENT_HDR_SIZE) {
sprintf(extra_str, "(len %d)", size);
print_packet(tv, cred, '*', index, NULL, COLOR_ERROR,
uint8_t flags = acl_flags(handle);
char handle_str[16], extra_str[32];
+ index_list[index].frame++;
+
if (size < HCI_ACL_HDR_SIZE) {
if (in)
print_packet(tv, cred, '*', index, NULL, COLOR_ERROR,
uint8_t flags = acl_flags(handle);
char handle_str[16], extra_str[32];
+ index_list[index].frame++;
+
if (size < HCI_SCO_HDR_SIZE) {
if (in)
print_packet(tv, cred, '*', index, NULL, COLOR_ERROR,
get_le32(&uuid[2]), get_le16(&uuid[0]));
}
-static void mgmt_print_enable(const char *label, uint8_t enable)
-{
- const char *str;
-
- switch (enable) {
- case 0x00:
- str = "Disabled";
- break;
- case 0x01:
- str = "Enabled";
- break;
- default:
- str = "Reserved";
- break;
- }
-
- print_field("%s: %s (0x%2.2x)", label, str, enable);
-}
-
static void mgmt_print_io_capability(uint8_t capability)
{
const char *str;
mgmt_print_address(data, address_type);
print_field("Min connection interval: %u", min_conn_interval);
print_field("Max connection interval: %u", max_conn_interval);
- print_field("Connection latency: %u", conn_latency);
+ print_conn_latency("Connection latency", conn_latency);
print_field("Supervision timeout: %u", supv_timeout);
}
{
uint8_t enable = get_u8(data);
- mgmt_print_enable("Powered", enable);
+ print_enable("Powered", enable);
}
static void mgmt_set_discoverable_cmd(const void *data, uint16_t size)
{
uint8_t enable = get_u8(data);
- mgmt_print_enable("Connectable", enable);
+ print_enable("Connectable", enable);
}
static void mgmt_set_fast_connectable_cmd(const void *data, uint16_t size)
{
uint8_t enable = get_u8(data);
- mgmt_print_enable("Fast Connectable", enable);
+ print_enable("Fast Connectable", enable);
}
static void mgmt_set_bondable_cmd(const void *data, uint16_t size)
{
uint8_t enable = get_u8(data);
- mgmt_print_enable("Bondable", enable);
+ print_enable("Bondable", enable);
}
static void mgmt_set_link_security_cmd(const void *data, uint16_t size)
{
uint8_t enable = get_u8(data);
- mgmt_print_enable("Link Security", enable);
+ print_enable("Link Security", enable);
}
static void mgmt_set_secure_simple_pairing_cmd(const void *data, uint16_t size)
{
uint8_t enable = get_u8(data);
- mgmt_print_enable("Secure Simple Pairing", enable);
+ print_enable("Secure Simple Pairing", enable);
}
static void mgmt_set_high_speed_cmd(const void *data, uint16_t size)
{
uint8_t enable = get_u8(data);
- mgmt_print_enable("High Speed", enable);
+ print_enable("High Speed", enable);
}
static void mgmt_set_low_energy_cmd(const void *data, uint16_t size)
{
uint8_t enable = get_u8(data);
- mgmt_print_enable("Low Energy", enable);
+ print_enable("Low Energy", enable);
}
static void mgmt_new_settings_rsp(const void *data, uint16_t size)
uint16_t num_keys = get_le16(data + 1);
int i;
- mgmt_print_enable("Debug keys", debug_keys);
+ print_enable("Debug keys", debug_keys);
print_field("Keys: %u", num_keys);
if (size - 3 != num_keys * 25) {
uint8_t disconnect = get_u8(data + 7);
mgmt_print_address(data, address_type);
- mgmt_print_enable("Disconnect", disconnect);
+ print_enable("Disconnect", disconnect);
}
static void mgmt_unpair_device_rsp(const void *data, uint16_t size)
{
uint8_t enable = get_u8(data);
- mgmt_print_enable("BR/EDR", enable);
+ print_enable("BR/EDR", enable);
}
static void mgmt_set_static_address_cmd(const void *data, uint16_t size)
{
uint8_t enable = get_u8(data);
- mgmt_print_enable("Configuration", enable);
+ print_enable("Configuration", enable);
}
static void mgmt_set_public_address_cmd(const void *data, uint16_t size)
uint8_t enable = get_u8(data + 1);
mgmt_print_address_type(type);
- mgmt_print_enable("Discovery", enable);
+ print_enable("Discovery", enable);
}
static void mgmt_device_blocked_evt(const void *data, uint16_t size)
#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
void print_le_set_adv_parameters_cmd(const void *data, uint8_t size)
{
- le_set_adv_parameters_cmd(data, size);
+ le_set_adv_parameters_cmd(data, size);
}
void print_le_set_random_address_cmd(const void *data, uint8_t size)
{
- le_set_random_address_cmd(data, size);
+ le_set_random_address_cmd(data, size);
}
void print_le_set_adv_data_cmd(const void *data, uint8_t size)
{
- le_set_adv_data_cmd(data, size);
+ le_set_adv_data_cmd(data, size);
}
void print_le_set_scan_rsp_data_cmd(const void *data, uint8_t size)
{
- le_set_scan_rsp_data_cmd(data, size);
+ le_set_scan_rsp_data_cmd(data, size);
}
void print_le_set_adv_enable_cmd(const void *data, uint8_t size)
{
- le_set_adv_enable_cmd(data, size);
+ le_set_adv_enable_cmd(data, size);
}
#endif
#define PACKET_FILTER_SHOW_TIME_OFFSET (1 << 3)
#define PACKET_FILTER_SHOW_ACL_DATA (1 << 4)
#define PACKET_FILTER_SHOW_SCO_DATA (1 << 5)
+#define PACKET_FILTER_SHOW_A2DP_STREAM (1 << 6)
+bool packet_has_filter(unsigned long filter);
void packet_set_filter(unsigned long filter);
void packet_add_filter(unsigned long filter);
void packet_del_filter(unsigned long filter);
#include <inttypes.h>
#include "lib/bluetooth.h"
+#include "lib/uuid.h"
#include "src/shared/util.h"
#include "bt.h"
#include "packet.h"
#include "display.h"
#include "l2cap.h"
-#include "uuid.h"
#include "keys.h"
#include "sdp.h"
#include "rfcomm.h"
uint16_t length;
uint8_t fcs;
uint8_t credits; /* only for UIH frame */
-} __attribute__((packed));
+};
struct rfcomm_lmsc {
uint8_t dlci;
uint8_t v24_sig;
uint8_t break_sig;
-} __attribute__((packed));
+};
struct rfcomm_rpn {
uint8_t dlci;
uint8_t xon;
uint8_t xoff;
uint16_t pm;
-} __attribute__ ((packed));
+};
struct rfcomm_rls {
uint8_t dlci;
uint8_t error;
-} __attribute__((packed));
+};
struct rfcomm_nsc {
uint8_t cmd_type;
-} __attribute__((packed));
+};
struct rfcomm_lmcc {
uint8_t type;
uint16_t length;
-} __attribute__((packed));
+};
struct rfcomm_frame {
struct rfcomm_lhdr hdr;
{
struct l2cap_frame *frame = &rfcomm_frame->l2cap_frame;
struct rfcomm_pn pn;
+ uint16_t mtu;
/* rfcomm_pn struct is defined in rfcomm.h */
if (!l2cap_frame_get_u8(frame, &pn.ack_timer))
return false;
- if (!l2cap_frame_get_le16(frame, &pn.mtu))
+ if (!l2cap_frame_get_le16(frame, &mtu))
return false;
+ pn.mtu = mtu;
+
if (!l2cap_frame_get_u8(frame, &pn.max_retrans))
return false;
#include <inttypes.h>
#include "lib/bluetooth.h"
+#include "lib/uuid.h"
#include "src/shared/util.h"
#include "packet.h"
#include "display.h"
#include "l2cap.h"
-#include "uuid.h"
#include "sdp.h"
#define MAX_TID 16
switch (size) {
case 2:
print_field("%*c%s (0x%4.4x)", indent, ' ',
- uuid16_to_str(get_be16(data)), get_be16(data));
+ bt_uuid16_to_str(get_be16(data)), get_be16(data));
break;
case 4:
print_field("%*c%s (0x%8.8x)", indent, ' ',
- uuid32_to_str(get_be32(data)), get_be32(data));
+ bt_uuid32_to_str(get_be32(data)), get_be32(data));
break;
case 16:
/* BASE_UUID = 00000000-0000-1000-8000-00805F9B34FB */
get_be16(data + 10) == 0x0080 &&
get_be32(data + 12) == 0x5F9B34FB)
print_field("%*c%s", indent, ' ',
- uuid32_to_str(get_be32(data)));
+ bt_uuid32_to_str(get_be32(data)));
break;
default:
packet_hexdump(data, size);
if (bt_string2uuid(&uuid, session->service) < 0)
goto failed;
+ sdp_uuid128_to_uuid(&uuid);
+
search = sdp_list_append(NULL, &uuid);
attrid = sdp_list_append(NULL, &range);
if (fcntl(fd, F_GETFD) < 0) {
error("bluetooth: fcntl(%d, F_GETFD): %s (%d)", fd,
strerror(errno), errno);
+ close(fd);
return invalid_args(msg);
}
io = g_io_channel_unix_new(fd);
- if (io == NULL)
+ if (io == NULL) {
+ close(fd);
return invalid_args(msg);
+ }
DBG("device %s", device);
#include <sys/types.h>
#include <dirent.h>
+#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include "messages.h"
+#define MSG_LIST_XML "mlisting.xml"
+
static char *root_folder = NULL;
struct session {
void *user_data;
};
+struct message_listing_data {
+ struct session *session;
+ const char *name;
+ uint16_t max;
+ uint16_t offset;
+ uint8_t subject_len;
+ uint16_t size;
+ char *path;
+ FILE *fp;
+ const struct messages_filter *filter;
+ messages_get_messages_listing_cb callback;
+ void *user_data;
+};
+
/* NOTE: Neither IrOBEX nor MAP specs says that folder listing needs to
* be sorted (in IrOBEX examples it is not). However existing implementations
* seem to follow the fig. 3-2 from MAP specification v1.0, and I've seen a
uint16_t num = 0;
uint16_t offs = 0;
- /* XXX: This isn't really documented for MAP. I need to take a look how
- * other implementations choose to deal with parent folder.
- */
- if (session->cwd[0] != 0 && fld->offset == 0) {
- num++;
- fld->callback(session, -EAGAIN, 0, "..", fld->user_data);
- } else {
- offs++;
- }
-
for (cur = list; offs < fld->offset; offs++) {
cur = cur->next;
if (cur == NULL)
return 0;
}
+static void max_msg_element(GMarkupParseContext *ctxt, const char *element,
+ const char **names, const char **values,
+ gpointer user_data, GError **gerr)
+{
+ struct message_listing_data *mld = user_data;
+ const char *key;
+ int i;
+
+ for (i = 0, key = names[i]; key; key = names[++i]) {
+ if (g_strcmp0(names[i], "handle") == 0) {
+ mld->size++;
+ break;
+ }
+ }
+}
+
+static void msg_element(GMarkupParseContext *ctxt, const char *element,
+ const char **names, const char **values,
+ gpointer user_data, GError **gerr)
+{
+ struct message_listing_data *mld = user_data;
+ struct messages_message *entry = NULL;
+ int i;
+
+ entry = g_new0(struct messages_message, 1);
+ if (mld->filter->parameter_mask == 0) {
+ entry->mask = (entry->mask | PMASK_SUBJECT \
+ | PMASK_DATETIME | PMASK_RECIPIENT_ADDRESSING \
+ | PMASK_SENDER_ADDRESSING \
+ | PMASK_ATTACHMENT_SIZE | PMASK_TYPE \
+ | PMASK_RECEPTION_STATUS);
+ } else
+ entry->mask = mld->filter->parameter_mask;
+
+ for (i = 0 ; names[i]; ++i) {
+ if (g_strcmp0(names[i], "handle") == 0) {
+ entry->handle = g_strdup(values[i]);
+ mld->size++;
+ continue;
+ }
+ if (g_strcmp0(names[i], "attachment_size") == 0) {
+ entry->attachment_size = g_strdup(values[i]);
+ continue;
+ }
+ if (g_strcmp0(names[i], "datetime") == 0) {
+ entry->datetime = g_strdup(values[i]);
+ continue;
+ }
+ if (g_strcmp0(names[i], "subject") == 0) {
+ entry->subject = g_strdup(values[i]);
+ continue;
+ }
+ if (g_strcmp0(names[i], "recipient_addressing") == 0) {
+ entry->recipient_addressing = g_strdup(values[i]);
+ continue;
+ }
+ if (g_strcmp0(names[i], "sender_addressing") == 0) {
+ entry->sender_addressing = g_strdup(values[i]);
+ continue;
+ }
+ if (g_strcmp0(names[i], "type") == 0) {
+ entry->type = g_strdup(values[i]);
+ continue;
+ }
+ if (g_strcmp0(names[i], "reception_status") == 0)
+ entry->reception_status = g_strdup(values[i]);
+ }
+
+ if (mld->size > mld->offset)
+ mld->callback(mld->session, -EAGAIN, mld->size, 0, entry, mld->user_data);
+
+ g_free(entry->reception_status);
+ g_free(entry->type);
+ g_free(entry->sender_addressing);
+ g_free(entry->subject);
+ g_free(entry->datetime);
+ g_free(entry->attachment_size);
+ g_free(entry->handle);
+ g_free(entry);
+}
+
+static const GMarkupParser msg_parser = {
+ msg_element,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+static const GMarkupParser max_msg_parser = {
+ max_msg_element,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+static gboolean get_messages_listing(void *d)
+{
+
+ struct message_listing_data *mld = d;
+ /* 1024 is the maximum size of the line which is calculated to be more
+ * sufficient*/
+ char buffer[1024];
+ GMarkupParseContext *ctxt;
+ size_t len;
+
+ while (fgets(buffer, 1024, mld->fp)) {
+ len = strlen(buffer);
+
+ if (mld->max == 0) {
+ ctxt = g_markup_parse_context_new(&max_msg_parser, 0, mld, NULL);
+ g_markup_parse_context_parse(ctxt, buffer, len, NULL);
+ g_markup_parse_context_free(ctxt);
+ } else {
+ ctxt = g_markup_parse_context_new(&msg_parser, 0, mld, NULL);
+ g_markup_parse_context_parse(ctxt, buffer, len, NULL);
+ g_markup_parse_context_free(ctxt);
+ }
+ }
+
+ if (mld->max == 0) {
+ mld->callback(mld->session, 0, mld->size, 0, NULL, mld->user_data);
+ goto done;
+ }
+
+ mld->callback(mld->session, 0, mld->size, 0, NULL, mld->user_data);
+
+done:
+ fclose(mld->fp);
+ return FALSE;
+}
+
int messages_get_messages_listing(void *session, const char *name,
uint16_t max, uint16_t offset,
uint8_t subject_len,
messages_get_messages_listing_cb callback,
void *user_data)
{
- return -ENOSYS;
+ struct message_listing_data *mld;
+ struct session *s = session;
+ char *path;
+
+ mld = g_new0(struct message_listing_data, 1);
+ mld->session = s;
+ mld->name = name;
+ mld->max = max;
+ mld->offset = offset;
+ mld->subject_len = subject_len;
+ mld->callback = callback;
+ mld->filter = filter;
+ mld->user_data = user_data;
+
+ path = g_build_filename(s->cwd_absolute, MSG_LIST_XML, NULL);
+ mld->fp = fopen(path, "r");
+ if (mld->fp == NULL) {
+ g_free(path);
+ messages_set_folder(s, mld->name, 0);
+ path = g_build_filename(s->cwd_absolute, MSG_LIST_XML, NULL);
+ mld->fp = fopen(path, "r");
+ if (mld->fp == NULL) {
+ int err = -errno;
+ DBG("fopen(): %d, %s", -err, strerror(-err));
+ g_free(path);
+ return -EBADR;
+ }
+ }
+
+
+ g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, get_messages_listing,
+ mld, g_free);
+ g_free(path);
+
+ return 0;
}
int messages_get_message(void *session, const char *handle,
/* Ignore all other parameter and return PhoneBookSize */
uint16_t size = g_slist_length(pbap->cache.entries);
+ pbap->obj->firstpacket = TRUE;
pbap->obj->apparam = g_obex_apparam_set_uint16(
pbap->obj->apparam,
PHONEBOOKSIZE_TAG,
*hi = G_OBEX_HDR_APPARAM;
#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
- if (pbap->params->maxlistcount == 0)
+ if (obj->firstpacket) {
+ obj->firstpacket = FALSE;
return g_obex_apparam_encode(obj->apparam, buf, mtu);
+ }
return 0;
#else
goto done;
dest[j++] = '\\';
+ /* fall through */
default:
dest[j] = src[i];
break;
struct obex_session *os = transfer->session;
const char *sender;
+ if (!agent)
+ return agent_does_not_exist(msg);
+
if (!os)
return invalid_args(msg);
{
sessions = g_slist_remove(sessions, os);
- if (os->io)
+ if (os->io) {
+ g_io_channel_shutdown(os->io, TRUE, NULL);
g_io_channel_unref(os->io);
+ }
if (os->obex)
g_obex_unref(os->obex);
return;
}
+ /* OPP session don't require CONNECT, in which case just call connect
+ * callback to register the transfer.
+ */
+ if (!os->service_data && os->service->service == OBEX_OPP) {
+ os->service_data = os->service->connect(os, &err);
+ if (err < 0) {
+ os_set_response(os, err);
+ return;
+ }
+ }
+
parse_type(os, req);
if (os->driver == NULL) {
*
*/
+#include <unistd.h>
+
#define OBJECT_SIZE_UNKNOWN -1
#define OBJECT_SIZE_DELETE -2
%define upgrade_script_path /usr/share/upgrade/scripts
Name: bluez
Summary: Bluetooth Stack for Linux
-Version: 5.43
+Version: 5.48
Release: 1
Group: Network & Connectivity/Bluetooth
License: GPL-2.0+ and LGPL-2.1+ and Apache-2.0
BuildRequires: pkgconfig(dbus-1)
BuildRequires: pkgconfig(iniparser)
BuildRequires: pkgconfig(libxml-2.0)
+BuildRequires: pkgconfig(json-c)
#BuildRequires: pkgconfig(glib-2.0)
#BuildRequires: pkgconfig(ncurses)
#BuildRequires: flex
--enable-tds=yes \
--enable-dbusoob \
--enable-test \
+ --enable-mesh=yes \
--with-telephony=tizen \
--enable-obex \
--enable-library \
--enable-health=yes \
--enable-dbusoob \
--enable-test \
+ --enable-mesh=yes \
--with-telephony=tizen \
--enable-obex \
--enable-library \
--enable-test \
--with-telephony=tizen \
--enable-obex \
+ --enable-mesh=yes \
--enable-library \
--enable-gatt \
--enable-experimental \
install -D -m 0755 tools/scotest $RPM_BUILD_ROOT/%{_bindir}/
install -D -m 0755 tools/bluemoon $RPM_BUILD_ROOT/%{_bindir}/
install -D -m 0755 attrib/gatttool $RPM_BUILD_ROOT/%{_bindir}/
+install -D -m 0755 mesh/meshctl $RPM_BUILD_ROOT/%{_bindir}/
install -D -m 0755 tools/obexctl %{buildroot}%{_bindir}/obexctl
%{_bindir}/l2test
%{_bindir}/rctest
%{_bindir}/bluetoothctl
+%{_bindir}/meshctl
%{_bindir}/btiotest
#%{_bindir}/mpris-player
%{_bindir}/bluetooth-player
return NULL;
}
- bt_gatt_client_set_ready_handler(conn->client,
- client_ready_callback, conn, NULL);
+ bt_gatt_client_ready_register(conn->client, client_ready_callback,
+ conn, NULL);
bt_gatt_client_set_service_changed(conn->client,
client_service_changed_callback, conn, NULL);
{
char addr[18];
char pinstr[7];
+ char name[25];
uint32_t class;
ba2str(device_get_address(device), addr);
class = btd_device_get_class(device);
- DBG("device %s 0x%x", addr, class);
+ device_get_name(device, name, sizeof(name));
+
+ DBG("device '%s' (%s) class: 0x%x vid/pid: 0x%X/0x%X",
+ name, addr, class,
+ btd_device_get_vendor (device),
+ btd_device_get_product (device));
+
+ /* The iCade shouldn't use random PINs like normal keyboards */
+ if (name != NULL && strstr(name, "iCade") != NULL)
+ return 0;
/* This is a class-based pincode guesser. Ignore devices with an
* unknown class.
if (attempt >= 4)
return 0;
- snprintf(pinstr, sizeof(pinstr), "%06d",
+ snprintf(pinstr, sizeof(pinstr), "%06u",
rand() % 1000000);
*display = true;
memcpy(pinbuf, pinstr, 6);
+++ /dev/null
-/*
- *
- * BlueZ - Bluetooth protocol stack for Linux
- *
- * Copyright (C) 2011 ST-Ericsson SA
- *
- * Author: Szymon Janc <szymon.janc@tieto.com> for ST-Ericsson
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
- */
-
-#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <errno.h>
-#include "gdbus/gdbus.h"
-
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/hci.h>
-#include <bluetooth/sdp.h>
-
-#include "src/plugin.h"
-#include "src/log.h"
-#include "src/dbus-common.h"
-#include "src/adapter.h"
-#include "src/device.h"
-#include "src/eir.h"
-#include "src/agent.h"
-#include "src/hcid.h"
-#include "src/error.h"
-
-#define OOB_INTERFACE "org.bluez.OutOfBand"
-
-struct oob_request {
- struct btd_adapter *adapter;
- DBusMessage *msg;
-};
-
-static GSList *oob_requests = NULL;
-static DBusConnection *connection = NULL;
-
-static gint oob_request_cmp(gconstpointer a, gconstpointer b)
-{
- const struct oob_request *data = a;
- const struct btd_adapter *adapter = b;
-
- return data->adapter != adapter;
-}
-
-static struct oob_request *find_oob_request(struct btd_adapter *adapter)
-{
- GSList *match;
-
- match = g_slist_find_custom(oob_requests, adapter, oob_request_cmp);
-
- if (match)
- return match->data;
-
- return NULL;
-}
-
-static void read_local_data_complete(struct btd_adapter *adapter,
- const uint8_t *hash192, const uint8_t *randomizer192,
- const uint8_t *hash256, const uint8_t *randomizer256,
- void *user_data)
-{
- struct DBusMessage *reply;
- struct oob_request *oob_request;
-
- oob_request = find_oob_request(adapter);
- if (!oob_request)
- return;
-
- if ((hash192 && randomizer192) || (hash256 && randomizer256))
- reply = g_dbus_create_reply(oob_request->msg,
- DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &hash192, 16,
- DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &randomizer192, 16,
- DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE,
- &hash256, hash256 ? 16 : 0,
- DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE,
- &randomizer256, randomizer256 ? 16 : 0,
- DBUS_TYPE_INVALID);
- else
- reply = btd_error_failed(oob_request->msg,
- "Failed to read local OOB data.");
-
- oob_requests = g_slist_remove(oob_requests, oob_request);
- dbus_message_unref(oob_request->msg);
- g_free(oob_request);
-
- if (!reply) {
- error("Couldn't allocate D-Bus message");
- return;
- }
-
- if (!g_dbus_send_message(connection, reply))
- error("D-Bus send failed");
-}
-
-static DBusMessage *read_local_data(DBusConnection *conn, DBusMessage *msg,
- void *data)
-{
- struct btd_adapter *adapter = data;
- struct oob_request *oob_request;
- struct oob_handler *handler;
-
- if (find_oob_request(adapter))
- return btd_error_in_progress(msg);
-
- if (btd_adapter_read_local_oob_data(adapter))
- return btd_error_failed(msg, "Request failed.");
-
- oob_request = g_new(struct oob_request, 1);
- oob_request->adapter = adapter;
- oob_requests = g_slist_append(oob_requests, oob_request);
- oob_request->msg = dbus_message_ref(msg);
-
- handler = g_new0(struct oob_handler, 1);
- handler->read_local_cb = read_local_data_complete;
-
- btd_adapter_set_oob_handler(oob_request->adapter, handler);
-
- return NULL;
-}
-
-static DBusMessage *add_remote_data(DBusConnection *conn, DBusMessage *msg,
- void *data)
-{
- struct btd_adapter *adapter = data;
- const char *addr = NULL;
- uint8_t *hash192 = NULL;
- uint8_t *randomizer192 = NULL;
- int32_t h192_len = 0;
- int32_t r192_len = 0;
- uint8_t *hash256 = NULL;
- uint8_t *randomizer256 = NULL;
- int32_t h256_len = 0;
- int32_t r256_len = 0;
- bdaddr_t bdaddr;
- uint8_t addr_type = BDADDR_BREDR;
- bool valid_len;
-
- if (!dbus_message_get_args(msg, NULL,
- DBUS_TYPE_STRING, &addr,
- DBUS_TYPE_BYTE, &addr_type,
- DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &hash192, &h192_len,
- DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &randomizer192, &r192_len,
- DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &hash256, &h256_len,
- DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &randomizer256, &r256_len,
- DBUS_TYPE_INVALID))
- return btd_error_invalid_args(msg);
-
- DBG("address type: %d", addr_type);
-
- valid_len = (h192_len == 16 && r192_len == 16) ||
- (h256_len == 16 && r256_len == 16);
-
- if (!valid_len || bachk(addr))
- return btd_error_invalid_args(msg);
-
- str2ba(addr, &bdaddr);
-
- if (btd_adapter_add_remote_oob_ext_data(adapter, &bdaddr, addr_type,
- hash192, randomizer192,
- hash256, randomizer256))
- return btd_error_failed(msg, "Request failed");
-
- return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
-}
-
-static DBusMessage *remove_remote_data(DBusConnection *conn, DBusMessage *msg,
- void *data)
-{
- struct btd_adapter *adapter = data;
- const char *addr;
- bdaddr_t bdaddr;
- uint8_t addr_type = 0;
-
- if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &addr,
- DBUS_TYPE_INVALID))
- return btd_error_invalid_args(msg);
-
- if (bachk(addr))
- return btd_error_invalid_args(msg);
-
- str2ba(addr, &bdaddr);
-
- if (btd_adapter_remove_remote_oob_ext_data(adapter, &bdaddr, addr_type))
- return btd_error_failed(msg, "Request failed");
-
- return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
-}
-
-static const GDBusMethodTable oob_methods[] = {
- { GDBUS_METHOD("AddRemoteData",
- GDBUS_ARGS({ "address", "s" },
- { "address_type", "y" },
- { "hash192", "ay" }, { "randomizer192", "ay" },
- { "hash256", "ay" }, { "randomizer256", "ay" }),
- NULL,
- add_remote_data) },
- { GDBUS_METHOD("RemoveRemoteData",
- GDBUS_ARGS({ "address", "s" }), NULL,
- remove_remote_data) },
- { GDBUS_ASYNC_METHOD("ReadLocalData",
- NULL, GDBUS_ARGS(
- {"hash192", "ay" }, { "randomizer192", "ay" },
- {"hash256", "ay" }, { "randomizer256", "ay" }),
- read_local_data) },
- { }
-};
-
-static int oob_probe(struct btd_adapter *adapter)
-{
- const char *path = adapter_get_path(adapter);
-
- DBG("dbusoob probe");
- DBG("adapter path: %s", path);
-
- if (!g_dbus_register_interface(connection, path, OOB_INTERFACE,
- oob_methods, NULL, NULL, adapter, NULL)) {
- error("OOB interface init failed on path %s", path);
- return -EIO;
- }
-
- return 0;
-}
-
-static void oob_remove(struct btd_adapter *adapter)
-{
- read_local_data_complete(adapter, NULL, NULL, NULL, NULL, NULL);
-
- g_dbus_unregister_interface(connection, adapter_get_path(adapter),
- OOB_INTERFACE);
-}
-
-static struct btd_adapter_driver oob_driver = {
- .name = "oob",
- .probe = oob_probe,
- .remove = oob_remove,
-};
-
-static int dbusoob_init(void)
-{
- DBG("Setup dbusoob plugin");
-
- connection = btd_get_dbus_connection();
-
- return btd_register_adapter_driver(&oob_driver);
-}
-
-static void dbusoob_exit(void)
-{
- DBG("Cleanup dbusoob plugin");
-
- btd_unregister_adapter_driver(&oob_driver);
-}
-
-BLUETOOTH_PLUGIN_DEFINE(dbusoob, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
- dbusoob_init, dbusoob_exit)
-#endif
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
+
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
struct policy_data *data = user_data;
struct btd_service *service;
- data->source_timer = 0;
+ data->sink_timer = 0;
data->sink_retries++;
service = btd_device_get_service(data->dev, A2DP_SINK_UUID);
*/
reconnect = reconnect_add(service);
- reconnect_reset(reconnect);
+ reconnect->active = false;
/*
* Should this device be reconnected? A matching UUID might not
reconnect_intervals_len = sizeof(default_intervals) /
sizeof(*reconnect_intervals);
reconnect_intervals = g_memdup(default_intervals,
- reconnect_intervals_len);
+ sizeof(default_intervals));
goto done;
}
&gerr);
if (gerr) {
g_clear_error(&gerr);
- reconnect_intervals_len = sizeof(default_intervals);
+ reconnect_intervals_len = sizeof(default_intervals) /
+ sizeof(*reconnect_intervals);
reconnect_intervals = g_memdup(default_intervals,
- reconnect_intervals_len);
+ sizeof(default_intervals));
}
auto_enable = g_key_file_get_boolean(conf, "Policy", "AutoEnable",
#include "src/adapter.h"
#include "src/device.h"
+#include "src/agent.h"
#include "src/plugin.h"
#include "src/log.h"
#include "src/shared/util.h"
+#include "profiles/input/sixaxis.h"
-static const struct {
- const char *name;
- uint16_t source;
- uint16_t vid;
- uint16_t pid;
- uint16_t version;
-} devices[] = {
- {
- .name = "PLAYSTATION(R)3 Controller",
- .source = 0x0002,
- .vid = 0x054c,
- .pid = 0x0268,
- .version = 0x0000,
- },
- {
- .name = "Navigation Controller",
- .source = 0x0002,
- .vid = 0x054c,
- .pid = 0x042f,
- .version = 0x0000,
- },
+struct authentication_closure {
+ guint auth_id;
+ char *sysfs_path;
+ struct btd_adapter *adapter;
+ struct btd_device *device;
+ int fd;
+ bdaddr_t bdaddr; /* device bdaddr */
+ CablePairingType type;
};
-struct leds_data {
- char *syspath_prefix;
- uint8_t bitmap;
+struct authentication_destroy_closure {
+ struct authentication_closure *closure;
+ bool remove_device;
};
-static void leds_data_destroy(struct leds_data *data)
-{
- free(data->syspath_prefix);
- free(data);
-}
-
static struct udev *ctx = NULL;
static struct udev_monitor *monitor = NULL;
static guint watch_id = 0;
+/* key = sysfs_path (const str), value = auth_closure */
+static GHashTable *pending_auths = NULL;
+
+#define SIXAXIS_HID_SDP_RECORD "3601920900000A000100000900013503191124090004"\
+ "350D35061901000900113503190011090006350909656E09006A090100090009350"\
+ "8350619112409010009000D350F350D350619010009001335031900110901002513"\
+ "576972656C65737320436F6E74726F6C6C65720901012513576972656C657373204"\
+ "36F6E74726F6C6C6572090102251B536F6E7920436F6D707574657220456E746572"\
+ "7461696E6D656E74090200090100090201090100090202080009020308210902042"\
+ "8010902052801090206359A35980822259405010904A101A1028501750895011500"\
+ "26FF00810375019513150025013500450105091901291381027501950D0600FF810"\
+ "3150026FF0005010901A10075089504350046FF0009300931093209358102C00501"\
+ "75089527090181027508953009019102750895300901B102C0A1028502750895300"\
+ "901B102C0A10285EE750895300901B102C0A10285EF750895300901B102C0C00902"\
+ "07350835060904090901000902082800090209280109020A280109020B090100090"\
+ "20C093E8009020D280009020E2800"
+
+/* Make sure to unset auth_id if already handled */
+static void auth_closure_destroy(struct authentication_closure *closure,
+ bool remove_device)
+{
+ if (closure->auth_id)
+ btd_cancel_authorization(closure->auth_id);
+
+ if (remove_device)
+ btd_adapter_remove_device(closure->adapter, closure->device);
+ close(closure->fd);
+ g_free(closure->sysfs_path);
+ g_free(closure);
+}
-static int get_device_bdaddr(int fd, bdaddr_t *bdaddr)
+static int sixaxis_get_device_bdaddr(int fd, bdaddr_t *bdaddr)
{
uint8_t buf[18];
int ret;
return 0;
}
-static int get_master_bdaddr(int fd, bdaddr_t *bdaddr)
+static int ds4_get_device_bdaddr(int fd, bdaddr_t *bdaddr)
{
- uint8_t buf[8];
+ uint8_t buf[7];
int ret;
memset(buf, 0, sizeof(buf));
- buf[0] = 0xf5;
+ buf[0] = 0x81;
ret = ioctl(fd, HIDIOCGFEATURE(sizeof(buf)), buf);
if (ret < 0) {
- error("sixaxis: failed to read master address (%s)",
- strerror(errno));
+ error("sixaxis: failed to read DS4 device address (%s)",
+ strerror(errno));
return ret;
}
- baswap(bdaddr, (bdaddr_t *) (buf + 2));
+ /* address is little-endian on DS4 */
+ bacpy(bdaddr, (bdaddr_t*) (buf + 1));
return 0;
}
-static int set_master_bdaddr(int fd, const bdaddr_t *bdaddr)
+static int get_device_bdaddr(int fd, bdaddr_t *bdaddr, CablePairingType type)
+{
+ if (type == CABLE_PAIRING_SIXAXIS)
+ return sixaxis_get_device_bdaddr(fd, bdaddr);
+ else if (type == CABLE_PAIRING_DS4)
+ return ds4_get_device_bdaddr(fd, bdaddr);
+ return -1;
+}
+
+static int sixaxis_get_master_bdaddr(int fd, bdaddr_t *bdaddr)
{
uint8_t buf[8];
int ret;
- buf[0] = 0xf5;
- buf[1] = 0x01;
+ memset(buf, 0, sizeof(buf));
- baswap((bdaddr_t *) (buf + 2), bdaddr);
+ buf[0] = 0xf5;
- ret = ioctl(fd, HIDIOCSFEATURE(sizeof(buf)), buf);
- if (ret < 0)
- error("sixaxis: failed to write master address (%s)",
+ ret = ioctl(fd, HIDIOCGFEATURE(sizeof(buf)), buf);
+ if (ret < 0) {
+ error("sixaxis: failed to read master address (%s)",
strerror(errno));
+ return ret;
+ }
- return ret;
+ baswap(bdaddr, (bdaddr_t *) (buf + 2));
+
+ return 0;
}
-static uint8_t calc_leds_bitmap(int number)
+static int ds4_get_master_bdaddr(int fd, bdaddr_t *bdaddr)
{
- uint8_t bitmap = 0;
+ uint8_t buf[16];
+ int ret;
- /* TODO we could support up to 10 (1 + 2 + 3 + 4) */
- if (number > 7)
- return bitmap;
+ memset(buf, 0, sizeof(buf));
+
+ buf[0] = 0x12;
- if (number > 4) {
- bitmap |= 0x10;
- number -= 4;
+ ret = ioctl(fd, HIDIOCGFEATURE(sizeof(buf)), buf);
+ if (ret < 0) {
+ error("sixaxis: failed to read DS4 master address (%s)",
+ strerror(errno));
+ return ret;
}
- bitmap |= 0x01 << number;
+ /* address is little-endian on DS4 */
+ bacpy(bdaddr, (bdaddr_t*) (buf + 10));
- return bitmap;
+ return 0;
+}
+
+static int get_master_bdaddr(int fd, bdaddr_t *bdaddr, CablePairingType type)
+{
+ if (type == CABLE_PAIRING_SIXAXIS)
+ return sixaxis_get_master_bdaddr(fd, bdaddr);
+ else if (type == CABLE_PAIRING_DS4)
+ return ds4_get_master_bdaddr(fd, bdaddr);
+ return -1;
}
-static void set_leds_hidraw(int fd, uint8_t leds_bitmap)
+static int sixaxis_set_master_bdaddr(int fd, const bdaddr_t *bdaddr)
{
- /*
- * the total time the led is active (0xff means forever)
- * | duty_length: cycle time in deciseconds (0 - "blink very fast")
- * | | ??? (Maybe a phase shift or duty_length multiplier?)
- * | | | % of duty_length led is off (0xff means 100%)
- * | | | | % of duty_length led is on (0xff means 100%)
- * | | | | |
- * 0xff, 0x27, 0x10, 0x00, 0x32,
- */
- uint8_t leds_report[] = {
- 0x01,
- 0x00, 0x00, 0x00, 0x00, 0x00, /* rumble values TBD */
- 0x00, 0x00, 0x00, 0x00, 0x00, /* LED_1=0x02, LED_2=0x04 ... */
- 0xff, 0x27, 0x10, 0x00, 0x32, /* LED_4 */
- 0xff, 0x27, 0x10, 0x00, 0x32, /* LED_3 */
- 0xff, 0x27, 0x10, 0x00, 0x32, /* LED_2 */
- 0xff, 0x27, 0x10, 0x00, 0x32, /* LED_1 */
- 0x00, 0x00, 0x00, 0x00, 0x00,
- };
+ uint8_t buf[8];
int ret;
- leds_report[10] = leds_bitmap;
+ buf[0] = 0xf5;
+ buf[1] = 0x01;
- ret = write(fd, leds_report, sizeof(leds_report));
- if (ret == sizeof(leds_report))
- return;
+ baswap((bdaddr_t *) (buf + 2), bdaddr);
+ ret = ioctl(fd, HIDIOCSFEATURE(sizeof(buf)), buf);
if (ret < 0)
- error("sixaxis: failed to set LEDS (%s)", strerror(errno));
- else
- error("sixaxis: failed to set LEDS (%d bytes written)", ret);
+ error("sixaxis: failed to write master address (%s)",
+ strerror(errno));
+
+ return ret;
}
-static bool set_leds_sysfs(struct leds_data *data)
+static int ds4_set_master_bdaddr(int fd, const bdaddr_t *bdaddr)
{
- int i;
+ uint8_t buf[23];
+ int ret;
- if (!data->syspath_prefix)
- return false;
+ buf[0] = 0x13;
+ bacpy((bdaddr_t*) (buf + 1), bdaddr);
+ /* TODO: we could put the key here but
+ there is no way to force a re-loading
+ of link keys to the kernel from here. */
+ memset(buf + 7, 0, 16);
- /* start from 1, LED0 is never used */
- for (i = 1; i <= 4; i++) {
- char path[PATH_MAX] = { 0 };
- char buf[2] = { 0 };
- int fd;
- int ret;
+ ret = ioctl(fd, HIDIOCSFEATURE(sizeof(buf)), buf);
+ if (ret < 0)
+ error("sixaxis: failed to write DS4 master address (%s)",
+ strerror(errno));
- snprintf(path, PATH_MAX, "%s%d/brightness",
- data->syspath_prefix, i);
+ return ret;
+}
- fd = open(path, O_WRONLY);
- if (fd < 0) {
- error("sixaxis: cannot open %s (%s)", path,
- strerror(errno));
- return false;
- }
+static int set_master_bdaddr(int fd, const bdaddr_t *bdaddr,
+ CablePairingType type)
+{
+ if (type == CABLE_PAIRING_SIXAXIS)
+ return sixaxis_set_master_bdaddr(fd, bdaddr);
+ else if (type == CABLE_PAIRING_DS4)
+ return ds4_set_master_bdaddr(fd, bdaddr);
+ return -1;
+}
- buf[0] = '0' + !!(data->bitmap & (1 << i));
- ret = write(fd, buf, sizeof(buf));
- close(fd);
- if (ret != sizeof(buf))
- return false;
+static bool is_auth_pending(struct authentication_closure *closure)
+{
+ GHashTableIter iter;
+ gpointer value;
+
+ g_hash_table_iter_init(&iter, pending_auths);
+ while (g_hash_table_iter_next(&iter, NULL, &value)) {
+ struct authentication_closure *c = value;
+ if (c == closure)
+ return true;
}
-
- return true;
+ return false;
}
-static gboolean setup_leds(GIOChannel *channel, GIOCondition cond,
- gpointer user_data)
+static gboolean auth_closure_destroy_idle(gpointer user_data)
{
- struct leds_data *data = user_data;
+ struct authentication_destroy_closure *destroy = user_data;
- if (!data)
- return FALSE;
+ auth_closure_destroy(destroy->closure, destroy->remove_device);
+ g_free(destroy);
- if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL))
- goto out;
-
- if(!set_leds_sysfs(data)) {
- int fd = g_io_channel_unix_get_fd(channel);
- set_leds_hidraw(fd, data->bitmap);
- }
-
-out:
- leds_data_destroy(data);
-
- return FALSE;
+ return false;
}
-static bool setup_device(int fd, int index, struct btd_adapter *adapter)
+static void agent_auth_cb(DBusError *derr, void *user_data)
{
- char device_addr[18], master_addr[18], adapter_addr[18];
- bdaddr_t device_bdaddr, master_bdaddr;
+ struct authentication_closure *closure = user_data;
+ struct authentication_destroy_closure *destroy;
+ char master_addr[18], adapter_addr[18], device_addr[18];
+ bdaddr_t master_bdaddr;
const bdaddr_t *adapter_bdaddr;
- struct btd_device *device;
+ bool remove_device = true;
- if (get_device_bdaddr(fd, &device_bdaddr) < 0)
- return false;
+ if (!is_auth_pending(closure))
+ return;
- if (get_master_bdaddr(fd, &master_bdaddr) < 0)
- return false;
+ /* Don't try to remove this auth, we're handling it already */
+ closure->auth_id = 0;
- /* This can happen if controller was plugged while already connected
- * eg. to charge up battery.
- * Don't set LEDs in that case, hence return false */
- device = btd_adapter_find_device(adapter, &device_bdaddr,
- BDADDR_BREDR);
- if (device && btd_device_is_connected(device))
- return false;
+ if (derr != NULL) {
+ DBG("Agent replied negatively, removing temporary device");
+ goto out;
+ }
- adapter_bdaddr = btd_adapter_get_address(adapter);
+ if (get_master_bdaddr(closure->fd, &master_bdaddr, closure->type) < 0)
+ goto out;
+ adapter_bdaddr = btd_adapter_get_address(closure->adapter);
if (bacmp(adapter_bdaddr, &master_bdaddr)) {
- if (set_master_bdaddr(fd, adapter_bdaddr) < 0)
- return false;
+ if (set_master_bdaddr(closure->fd, adapter_bdaddr,
+ closure->type) < 0)
+ goto out;
}
- ba2str(&device_bdaddr, device_addr);
+ remove_device = false;
+ btd_device_set_trusted(closure->device, true);
+ btd_device_set_temporary(closure->device, false);
+
+ if (closure->type == CABLE_PAIRING_SIXAXIS)
+ btd_device_set_record(closure->device, HID_UUID,
+ SIXAXIS_HID_SDP_RECORD);
+
+ ba2str(&closure->bdaddr, device_addr);
ba2str(&master_bdaddr, master_addr);
ba2str(adapter_bdaddr, adapter_addr);
DBG("remote %s old_master %s new_master %s",
device_addr, master_addr, adapter_addr);
- device = btd_adapter_get_device(adapter, &device_bdaddr, BDADDR_BREDR);
-
- if (g_slist_find_custom(btd_device_get_uuids(device), HID_UUID,
- (GCompareFunc)strcasecmp)) {
- DBG("device %s already known, skipping", device_addr);
- return true;
- }
-
- info("sixaxis: setting up new device");
-
- btd_device_device_set_name(device, devices[index].name);
- btd_device_set_pnpid(device, devices[index].source, devices[index].vid,
- devices[index].pid, devices[index].version);
- btd_device_set_temporary(device, false);
-
- return true;
-}
-
-static int get_js_number(struct udev_device *udevice)
-{
- struct udev_list_entry *devices, *dev_list_entry;
- struct udev_enumerate *enumerate;
- struct udev_device *hid_parent;
- const char *hidraw_node;
- const char *hid_id;
- int number = 0;
-
- hid_parent = udev_device_get_parent_with_subsystem_devtype(udevice,
- "hid", NULL);
-
- /*
- * Look for HID_UNIQ first for the correct behavior via BT, if
- * HID_UNIQ is not available it means the USB bus is being used and we
- * can rely on HID_PHYS.
- */
- hid_id = udev_device_get_property_value(hid_parent, "HID_UNIQ");
- if (!hid_id)
- hid_id = udev_device_get_property_value(hid_parent,
- "HID_PHYS");
-
- hidraw_node = udev_device_get_devnode(udevice);
- if (!hid_id || !hidraw_node)
- return 0;
-
- enumerate = udev_enumerate_new(udev_device_get_udev(udevice));
- udev_enumerate_add_match_sysname(enumerate, "js*");
- udev_enumerate_scan_devices(enumerate);
- devices = udev_enumerate_get_list_entry(enumerate);
-
- udev_list_entry_foreach(dev_list_entry, devices) {
- struct udev_device *input_parent;
- struct udev_device *js_dev;
- const char *input_id;
- const char *devname;
-
- devname = udev_list_entry_get_name(dev_list_entry);
- js_dev = udev_device_new_from_syspath(
- udev_device_get_udev(udevice),
- devname);
-
- input_parent = udev_device_get_parent_with_subsystem_devtype(
- js_dev, "input", NULL);
- if (!input_parent)
- goto next;
-
- /* check if this is the joystick relative to the hidraw device
- * above */
- input_id = udev_device_get_sysattr_value(input_parent, "uniq");
-
- /*
- * A strlen() check is needed because input device over USB
- * have the UNIQ attribute defined but with an empty value.
- */
- if (!input_id || strlen(input_id) == 0)
- input_id = udev_device_get_sysattr_value(input_parent,
- "phys");
-
- if (!input_id)
- goto next;
-
- if (!strcmp(input_id, hid_id)) {
- number = atoi(udev_device_get_sysnum(js_dev));
-
- /* joystick numbers start from 0, leds from 1 */
- number++;
-
- udev_device_unref(js_dev);
- break;
- }
-next:
- udev_device_unref(js_dev);
- }
-
- udev_enumerate_unref(enumerate);
-
- return number;
+out:
+ g_hash_table_steal(pending_auths, closure->sysfs_path);
+
+ /* btd_adapter_remove_device() cannot be called in this
+ * callback or it would lead to a double-free in while
+ * trying to cancel the authentication that's being processed,
+ * so clean up in an idle */
+ destroy = g_new0(struct authentication_destroy_closure, 1);
+ destroy->closure = closure;
+ destroy->remove_device = remove_device;
+ g_idle_add(auth_closure_destroy_idle, destroy);
}
-static char *get_leds_syspath_prefix(struct udev_device *udevice)
+static bool setup_device(int fd, const char *sysfs_path,
+ const struct cable_pairing *cp,
+ struct btd_adapter *adapter)
{
- struct udev_list_entry *dev_list_entry;
- struct udev_enumerate *enumerate;
- struct udev_device *hid_parent;
- const char *syspath;
- char *syspath_prefix;
-
- hid_parent = udev_device_get_parent_with_subsystem_devtype(udevice,
- "hid", NULL);
+ bdaddr_t device_bdaddr;
+ const bdaddr_t *adapter_bdaddr;
+ struct btd_device *device;
+ struct authentication_closure *closure;
- enumerate = udev_enumerate_new(udev_device_get_udev(udevice));
- udev_enumerate_add_match_parent(enumerate, hid_parent);
- udev_enumerate_add_match_subsystem(enumerate, "leds");
- udev_enumerate_scan_devices(enumerate);
+ if (get_device_bdaddr(fd, &device_bdaddr, cp->type) < 0)
+ return false;
- dev_list_entry = udev_enumerate_get_list_entry(enumerate);
- if (!dev_list_entry) {
- syspath_prefix = NULL;
- goto out;
+ /* This can happen if controller was plugged while already setup and
+ * connected eg. to charge up battery. */
+ device = btd_adapter_find_device(adapter, &device_bdaddr,
+ BDADDR_BREDR);
+ if (device != NULL &&
+ btd_device_is_connected(device) &&
+ g_slist_find_custom(btd_device_get_uuids(device), HID_UUID,
+ (GCompareFunc)strcasecmp)) {
+ char device_addr[18];
+ ba2str(&device_bdaddr, device_addr);
+ DBG("device %s already known, skipping", device_addr);
+ return false;
}
- syspath = udev_list_entry_get_name(dev_list_entry);
-
- /*
- * All the sysfs paths of the LEDs have the same structure, just the
- * number changes, so strip it and store only the common prefix.
- *
- * Subtracting 1 here means assuming that the LED number is a single
- * digit, this is safe as the kernel driver only exposes 4 LEDs.
- */
- syspath_prefix = strndup(syspath, strlen(syspath) - 1);
-
-out:
- udev_enumerate_unref(enumerate);
-
- return syspath_prefix;
-}
-
-static struct leds_data *get_leds_data(struct udev_device *udevice)
-{
- struct leds_data *data;
- int number;
+ device = btd_adapter_get_device(adapter, &device_bdaddr, BDADDR_BREDR);
- number = get_js_number(udevice);
- DBG("number %d", number);
+ info("sixaxis: setting up new device");
- data = malloc0(sizeof(*data));
- if (!data)
- return NULL;
+ btd_device_device_set_name(device, cp->name);
+ btd_device_set_pnpid(device, cp->source, cp->vid, cp->pid, cp->version);
+ btd_device_set_temporary(device, true);
- data->bitmap = calc_leds_bitmap(number);
- if (data->bitmap == 0) {
- leds_data_destroy(data);
- return NULL;
+ closure = g_new0(struct authentication_closure, 1);
+ if (!closure) {
+ btd_adapter_remove_device(adapter, device);
+ return false;
}
+ closure->adapter = adapter;
+ closure->device = device;
+ closure->sysfs_path = g_strdup(sysfs_path);
+ closure->fd = fd;
+ bacpy(&closure->bdaddr, &device_bdaddr);
+ closure->type = cp->type;
+ adapter_bdaddr = btd_adapter_get_address(adapter);
+ closure->auth_id = btd_request_authorization_cable_configured(
+ adapter_bdaddr, &device_bdaddr,
+ HID_UUID, agent_auth_cb, closure);
- /*
- * It's OK if this fails, set_leds_hidraw() will be used in
- * case data->syspath_prefix is NULL.
- */
- data->syspath_prefix = get_leds_syspath_prefix(udevice);
+ g_hash_table_insert(pending_auths, closure->sysfs_path, closure);
- return data;
+ return true;
}
-static int get_supported_device(struct udev_device *udevice, uint16_t *bus)
+static const struct cable_pairing *
+get_pairing_type_for_device(struct udev_device *udevice, uint16_t *bus,
+ char **sysfs_path)
{
struct udev_device *hid_parent;
- uint16_t vid, pid;
const char *hid_id;
- guint i;
+ const struct cable_pairing *cp;
+ uint16_t vid, pid;
hid_parent = udev_device_get_parent_with_subsystem_devtype(udevice,
"hid", NULL);
if (!hid_parent)
- return -1;
+ return NULL;
hid_id = udev_device_get_property_value(hid_parent, "HID_ID");
if (sscanf(hid_id, "%hx:%hx:%hx", bus, &vid, &pid) != 3)
- return -1;
+ return NULL;
- for (i = 0; i < G_N_ELEMENTS(devices); i++) {
- if (devices[i].vid == vid && devices[i].pid == pid)
- return i;
- }
+ cp = get_pairing(vid, pid);
+ *sysfs_path = g_strdup(udev_device_get_syspath(udevice));
- return -1;
+ return cp;
}
static void device_added(struct udev_device *udevice)
{
struct btd_adapter *adapter;
- GIOChannel *io;
uint16_t bus;
- int index;
+ char *sysfs_path = NULL;
+ const struct cable_pairing *cp;
int fd;
adapter = btd_adapter_get_default();
if (!adapter)
return;
- index = get_supported_device(udevice, &bus);
- if (index < 0)
+ cp = get_pairing_type_for_device(udevice, &bus, &sysfs_path);
+ if (!cp || (cp->type != CABLE_PAIRING_SIXAXIS &&
+ cp->type != CABLE_PAIRING_DS4))
+ return;
+ if (bus != BUS_USB)
return;
- info("sixaxis: compatible device connected: %s (%04X:%04X)",
- devices[index].name, devices[index].vid,
- devices[index].pid);
+ info("sixaxis: compatible device connected: %s (%04X:%04X %s)",
+ cp->name, cp->vid, cp->pid, sysfs_path);
fd = open(udev_device_get_devnode(udevice), O_RDWR);
- if (fd < 0)
+ if (fd < 0) {
+ g_free(sysfs_path);
return;
+ }
- io = g_io_channel_unix_new(fd);
+ /* Only close the fd if an authentication is not pending */
+ if (!setup_device(fd, sysfs_path, cp, adapter))
+ close(fd);
- switch (bus) {
- case BUS_USB:
- if (!setup_device(fd, index, adapter))
- break;
+ g_free(sysfs_path);
+}
- /* fall through */
- case BUS_BLUETOOTH:
- /* wait for events before setting leds */
- g_io_add_watch(io, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
- setup_leds, get_leds_data(udevice));
+static void device_removed(struct udev_device *udevice)
+{
+ struct authentication_closure *closure;
+ const char *sysfs_path;
- break;
- default:
- DBG("uknown bus type (%u)", bus);
- break;
- }
+ sysfs_path = udev_device_get_syspath(udevice);
+ if (!sysfs_path)
+ return;
+
+ closure = g_hash_table_lookup(pending_auths, sysfs_path);
+ if (!closure)
+ return;
- g_io_channel_set_close_on_unref(io, TRUE);
- g_io_channel_unref(io);
+ g_hash_table_steal(pending_auths, sysfs_path);
+ auth_closure_destroy(closure, true);
}
static gboolean monitor_watch(GIOChannel *source, GIOCondition condition,
if (!g_strcmp0(udev_device_get_action(udevice), "add"))
device_added(udevice);
+ else if (!g_strcmp0(udev_device_get_action(udevice), "remove"))
+ device_removed(udevice);
udev_device_unref(udevice);
watch_id = g_io_add_watch(channel, G_IO_IN, monitor_watch, NULL);
g_io_channel_unref(channel);
+ pending_auths = g_hash_table_new(g_str_hash,
+ g_str_equal);
+
return 0;
}
static void sixaxis_exit(void)
{
+ GHashTableIter iter;
+ gpointer value;
+
DBG("");
+ g_hash_table_iter_init(&iter, pending_auths);
+ while (g_hash_table_iter_next(&iter, NULL, &value)) {
+ struct authentication_closure *closure = value;
+ auth_closure_destroy(closure, true);
+ }
+ g_hash_table_destroy(pending_auths);
+ pending_auths = NULL;
+
g_source_remove(watch_id);
watch_id = 0;
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
+
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
gboolean reconfigure;
gboolean start;
GSList *cb;
+ GIOChannel *io;
int ref;
};
{
DBG("%p", s);
+ if (s->io) {
+ g_io_channel_shutdown(s->io, TRUE, NULL);
+ g_io_channel_unref(s->io);
+ }
+
setups = g_slist_remove(setups, s);
if (s->session)
avdtp_unref(s->session);
}
auto_config(setup);
+ setup_unref(setup);
}
#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
ret = a2dp_sep->endpoint->set_configuration(a2dp_sep,
codec->data,
cap->length - sizeof(*codec),
- setup,
+ setup_ref(setup),
endpoint_setconf_cb,
a2dp_sep->user_data);
if (ret == 0)
return TRUE;
+ setup_unref(setup);
setup->err = g_new(struct avdtp_error, 1);
avdtp_error_init(setup->err, AVDTP_MEDIA_CODEC,
AVDTP_UNSUPPORTED_CONFIGURATION);
if (ret == FALSE) {
setup->stream = NULL;
finalize_setup_errno(setup, -EPERM, finalize_config, NULL);
- return;
+ goto done;
}
err = avdtp_open(setup->session, setup->stream);
if (err == 0)
- return;
+ goto done;
error("Error on avdtp_open %s (%d)", strerror(-err), -err);
setup->stream = NULL;
finalize_setup_errno(setup, err, finalize_config, NULL);
+done:
+ setup_unref(setup);
}
static void setconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
err = a2dp_sep->endpoint->set_configuration(a2dp_sep,
codec->data, service->length -
sizeof(*codec),
- setup,
+ setup_ref(setup),
endpoint_open_cb,
a2dp_sep->user_data);
if (err == 0)
setup->stream = NULL;
finalize_setup_errno(setup, -EPERM, finalize_config, NULL);
+ setup_unref(setup);
return;
}
{
struct a2dp_server *server;
struct a2dp_channel *chan;
+ const struct queue_entry *entry;
server = find_server(servers, device_get_adapter(device));
if (server == NULL)
if (chan->session)
return avdtp_ref(chan->session);
+ /* Check if there is any SEP available */
+ for (entry = queue_get_entries(server->seps); entry;
+ entry = entry->next) {
+ struct avdtp_local_sep *sep = entry->data;
+
+ if (avdtp_sep_get_state(sep) == AVDTP_STATE_IDLE)
+ goto found;
+ }
+
+ DBG("Unable to find any available SEP");
+
+ return NULL;
+
+found:
chan->session = avdtp_new(NULL, device, server->seps);
if (!chan->session) {
channel_remove(chan);
g_io_channel_set_close_on_unref(io, FALSE);
+ g_io_channel_unref(setup->io);
+ setup->io = NULL;
+
setup_unref(setup);
return;
if (!setup || !setup->stream)
goto drop;
+ if (setup->io) {
+ error("transport channel already exists");
+ goto drop;
+ }
+
if (!bt_io_accept(io, transport_cb, setup, NULL, &err)) {
error("bt_io_accept: %s", err->message);
g_error_free(err);
goto drop;
}
+ /*
+ * Reference the channel so it can be shutdown properly
+ * stopping bt_io_accept from calling the callback with invalid
+ * setup pointer.
+ */
+ setup->io = g_io_channel_ref(io);
+
return;
}
}
#else
server->io = bt_io_listen(NULL, confirm_cb, server, NULL, &err,
- BT_IO_OPT_SOURCE_BDADDR,
- btd_adapter_get_address(server->adapter),
- BT_IO_OPT_PSM, AVDTP_PSM,
- BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
- BT_IO_OPT_MASTER, true,
- BT_IO_OPT_INVALID);
+ BT_IO_OPT_SOURCE_BDADDR,
+ btd_adapter_get_address(server->adapter),
+ BT_IO_OPT_PSM, AVDTP_PSM,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+ BT_IO_OPT_MASTER, true,
+ BT_IO_OPT_INVALID);
#endif
if (server->io)
return true;
done:
finalize_select(setup);
+ setup_unref(setup);
}
static struct a2dp_sep *a2dp_find_sep(struct avdtp *session, GSList *list,
err = sep->endpoint->select_configuration(sep, codec->data,
service->length - sizeof(*codec),
- setup,
+ setup_ref(setup),
select_cb, sep->user_data);
if (err == 0)
return cb_data->id;
+ setup_unref(setup);
+
fail:
setup_cb_free(cb_data);
return 0;
*/
#define AVC_PRESS_TIMEOUT 2
+#define CONTROL_TIMEOUT 10
+#define BROWSING_TIMEOUT 10
+
+#define PASSTHROUGH_QUEUE 0
+#define CONTROL_QUEUE 1
+
#define QUIRK_NO_RELEASE 1 << 0
/* Message types */
typedef int (*avctp_process_cb) (void *data);
struct avctp_pending_req {
- struct avctp_channel *chan;
+ struct avctp_queue *queue;
uint8_t transaction;
guint timeout;
+ bool retry;
int err;
avctp_process_cb process;
void *data;
GDestroyNotify destroy;
};
+struct avctp_queue {
+ struct avctp_channel *chan;
+ struct avctp_pending_req *p;
+ GQueue *queue;
+ guint process_id;
+};
+
struct avctp_channel {
struct avctp *session;
GIOChannel *io;
uint16_t omtu;
uint8_t *buffer;
GSList *handlers;
- struct avctp_pending_req *p;
- GQueue *queue;
+ GSList *queues;
GSList *processed;
- guint process_id;
GDestroyNotify destroy;
};
g_free(req);
}
+static void avctp_queue_destroy(void *data)
+{
+ struct avctp_queue *queue = data;
+
+ if (queue->process_id > 0)
+ g_source_remove(queue->process_id);
+
+ if (queue->p)
+ pending_destroy(queue->p, NULL);
+
+ g_queue_foreach(queue->queue, pending_destroy, NULL);
+ g_queue_free(queue->queue);
+ g_free(queue);
+}
+
static void avctp_channel_destroy(struct avctp_channel *chan)
{
g_io_channel_shutdown(chan->io, TRUE, NULL);
if (chan->watch)
g_source_remove(chan->watch);
- if (chan->p)
- pending_destroy(chan->p, NULL);
-
- if (chan->process_id > 0)
- g_source_remove(chan->process_id);
-
if (chan->destroy)
chan->destroy(chan);
g_free(chan->buffer);
- g_queue_foreach(chan->queue, pending_destroy, NULL);
- g_queue_free(chan->queue);
+ g_slist_free_full(chan->queues, avctp_queue_destroy);
g_slist_foreach(chan->processed, pending_destroy, NULL);
g_slist_free(chan->processed);
g_slist_free_full(chan->handlers, g_free);
}
}
+static uint8_t chan_get_transaction(struct avctp_channel *chan)
+{
+ GSList *l, *tmp;
+ uint8_t transaction;
+
+ if (!chan->processed)
+ goto done;
+
+ tmp = g_slist_copy(chan->processed);
+
+ /* Find first unused transaction id */
+ for (l = tmp; l; l = g_slist_next(l)) {
+ struct avctp_pending_req *req = l->data;
+
+ if (req->transaction == chan->transaction) {
+ chan->transaction++;
+ chan->transaction %= 16;
+ tmp = g_slist_delete_link(tmp, l);
+ l = tmp;
+ }
+ }
+
+ g_slist_free(tmp);
+
+done:
+ transaction = chan->transaction;
+
+ chan->transaction++;
+ chan->transaction %= 16;
+
+ return transaction;
+}
+
static int avctp_send(struct avctp_channel *control, uint8_t transaction,
uint8_t cr, uint8_t code,
uint8_t subunit, uint8_t opcode,
avctp = (void *) control->buffer;
avc = (void *) avctp + sizeof(*avctp);
+ if (transaction > 16)
+ transaction = chan_get_transaction(control);
+
avctp->transaction = transaction;
avctp->packet_type = AVCTP_PACKET_SINGLE;
avctp->cr = cr;
return err;
}
-static int avctp_browsing_send(struct avctp_channel *browsing,
+static int avctp_browsing_send(struct avctp_queue *queue,
uint8_t transaction, uint8_t cr,
uint8_t *operands, size_t operand_count)
{
+ struct avctp_channel *browsing = queue->chan;
struct avctp_header *avctp;
struct msghdr msg;
struct iovec iov[2];
{
struct avctp_control_req *req = data;
struct avctp_pending_req *p = req->p;
- struct avctp *session = p->chan->session;
+ struct avctp *session = p->queue->chan->session;
if (p->err == 0 || req->func == NULL)
goto done;
{
struct avctp_browsing_req *req = data;
struct avctp_pending_req *p = req->p;
- struct avctp *session = p->chan->session;
+ struct avctp *session = p->queue->chan->session;
if (p->err == 0 || req->func == NULL)
goto done;
static gboolean req_timeout(gpointer user_data)
{
- struct avctp_channel *chan = user_data;
- struct avctp_pending_req *p = chan->p;
+ struct avctp_queue *queue = user_data;
+ struct avctp_pending_req *p = queue->p;
- DBG("transaction %u", p->transaction);
+ DBG("transaction %u retry %s", p->transaction, p->retry ? "true" :
+ "false");
p->timeout = 0;
+
+ if (p->retry) {
+ p->process(p->data);
+ return FALSE;
+ }
+
p->err = -ETIMEDOUT;
pending_destroy(p, NULL);
- chan->p = NULL;
+ queue->p = NULL;
- if (chan->process_id == 0)
- chan->process_id = g_idle_add(process_queue, chan);
+ if (queue->process_id == 0)
+ queue->process_id = g_idle_add(process_queue, queue);
return FALSE;
}
+static int process_passthrough(void *data)
+{
+ struct avctp_control_req *req = data;
+ struct avctp_pending_req *p = req->p;
+ int ret;
+
+ ret = avctp_send(p->queue->chan, p->transaction, AVCTP_COMMAND,
+ req->code, req->subunit, req->op, req->operands,
+ req->operand_count);
+ if (ret < 0)
+ return ret;
+
+ p->timeout = g_timeout_add_seconds(AVC_PRESS_TIMEOUT, req_timeout,
+ p->queue);
+
+ return 0;
+}
+
static int process_control(void *data)
{
struct avctp_control_req *req = data;
struct avctp_pending_req *p = req->p;
+ int ret;
- return avctp_send(p->chan, p->transaction, AVCTP_COMMAND, req->code,
- req->subunit, req->op,
- req->operands, req->operand_count);
+ ret = avctp_send(p->queue->chan, p->transaction, AVCTP_COMMAND,
+ req->code, req->subunit, req->op, req->operands,
+ req->operand_count);
+ if (ret < 0)
+ return ret;
+
+ p->retry = !p->retry;
+
+ p->timeout = g_timeout_add_seconds(CONTROL_TIMEOUT, req_timeout,
+ p->queue);
+
+ return 0;
}
static int process_browsing(void *data)
{
struct avctp_browsing_req *req = data;
struct avctp_pending_req *p = req->p;
+ int ret;
- return avctp_browsing_send(p->chan, p->transaction, AVCTP_COMMAND,
+ ret = avctp_browsing_send(p->queue, p->transaction, AVCTP_COMMAND,
req->operands, req->operand_count);
+ if (ret < 0)
+ return ret;
+
+ p->timeout = g_timeout_add_seconds(BROWSING_TIMEOUT, req_timeout,
+ p->queue);
+
+ return 0;
+}
+
+static gboolean process_queue(void *user_data)
+{
+ struct avctp_queue *queue = user_data;
+ struct avctp_pending_req *p = queue->p;
+
+ queue->process_id = 0;
+
+ if (p != NULL)
+ return FALSE;
+
+ while ((p = g_queue_pop_head(queue->queue))) {
+
+ if (p->process(p->data) == 0)
+ break;
+
+ pending_destroy(p, NULL);
+ }
+
+ if (p == NULL)
+ return FALSE;
+
+ queue->p = p;
+
+ return FALSE;
+
}
+#if 0
static gboolean process_queue(void *user_data)
{
- struct avctp_channel *chan = user_data;
- struct avctp_pending_req *p = chan->p;
+ struct avctp_queue *queue = user_data;
+ struct avctp_pending_req *p = queue->p;
- chan->process_id = 0;
+ queue->process_id = 0;
if (p != NULL)
return FALSE;
- while ((p = g_queue_pop_head(chan->queue))) {
+ while ((p = g_queue_pop_head(queue->queue))) {
if (p->process(p->data) == 0)
break;
if (p == NULL)
return FALSE;
- chan->p = p;
+ queue->p = p;
#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
p->timeout = g_timeout_add_seconds(5, req_timeout, chan);
-#else
- p->timeout = g_timeout_add_seconds(2, req_timeout, chan);
#endif
return FALSE;
}
+#endif
static void control_response(struct avctp_channel *control,
struct avctp_header *avctp,
uint8_t *operands,
size_t operand_count)
{
- struct avctp_pending_req *p = control->p;
+ struct avctp_pending_req *p;
struct avctp_control_req *req;
+ struct avctp_queue *queue;
GSList *l;
+ if (avc->opcode == AVC_OP_PASSTHROUGH)
+ queue = g_slist_nth_data(control->queues, PASSTHROUGH_QUEUE);
+ else
+ queue = g_slist_nth_data(control->queues, CONTROL_QUEUE);
+
+ p = queue->p;
+
if (p && p->transaction == avctp->transaction) {
+ req = p->data;
+ if (req->op != avc->opcode)
+ goto done;
+
control->processed = g_slist_prepend(control->processed, p);
if (p->timeout > 0) {
p->timeout = 0;
}
- control->p = NULL;
+ queue->p = NULL;
- if (control->process_id == 0)
- control->process_id = g_idle_add(process_queue,
- control);
+ if (queue->process_id == 0)
+ queue->process_id = g_idle_add(process_queue, queue);
}
+done:
for (l = control->processed; l; l = l->next) {
p = l->data;
req = p->data;
if (p->transaction != avctp->transaction)
continue;
+ if (req->op != avc->opcode)
+ continue;
+
if (req->func && req->func(control->session, avc->code,
avc->subunit_type, p->transaction,
operands, operand_count,
uint8_t *operands,
size_t operand_count)
{
- struct avctp_pending_req *p = browsing->p;
+ struct avctp_pending_req *p;
struct avctp_browsing_req *req;
+ struct avctp_queue *queue;
GSList *l;
+ queue = g_slist_nth_data(browsing->queues, 0);
+
+ p = queue->p;
+
if (p && p->transaction == avctp->transaction) {
browsing->processed = g_slist_prepend(browsing->processed, p);
p->timeout = 0;
}
- browsing->p = NULL;
+ queue->p = NULL;
- if (browsing->process_id == 0)
- browsing->process_id = g_idle_add(process_queue,
- browsing);
+ if (queue->process_id == 0)
+ queue->process_id = g_idle_add(process_queue, queue);
}
for (l = browsing->processed; l; l = l->next) {
DBG("AVRCP: uinput initialized for %s", address);
}
+static struct avctp_queue *avctp_queue_create(struct avctp_channel *chan)
+{
+ struct avctp_queue *queue;
+
+ queue = g_new0(struct avctp_queue, 1);
+ queue->chan = chan;
+ queue->queue = g_queue_new();
+
+ return queue;
+}
+
static struct avctp_channel *avctp_channel_create(struct avctp *session,
GIOChannel *io,
+ int queues,
GDestroyNotify destroy)
{
struct avctp_channel *chan;
chan = g_new0(struct avctp_channel, 1);
chan->session = session;
chan->io = g_io_channel_ref(io);
- chan->queue = g_queue_new();
chan->destroy = destroy;
+ while (queues--) {
+ struct avctp_queue *queue;
+
+ queue = avctp_queue_create(chan);
+ chan->queues = g_slist_prepend(chan->queues, queue);
+ }
+
return chan;
}
{
struct avctp *session = data;
struct avctp_channel *browsing = session->browsing;
+ struct avctp_queue *queue;
char address[18];
uint16_t imtu, omtu;
GError *gerr = NULL;
DBG("AVCTP Browsing: connected to %s", address);
if (browsing == NULL) {
- browsing = avctp_channel_create(session, chan,
+ browsing = avctp_channel_create(session, chan, 1,
avctp_destroy_browsing);
session->browsing = browsing;
}
avctp_set_state(session, AVCTP_STATE_BROWSING_CONNECTED, 0);
/* Process any request that was pending the connection to complete */
- if (browsing->process_id == 0 && !g_queue_is_empty(browsing->queue))
- browsing->process_id = g_idle_add(process_queue, browsing);
+ queue = g_slist_nth_data(browsing->queues, 0);
+ if (queue->process_id == 0 && !g_queue_is_empty(queue->queue))
+ queue->process_id = g_idle_add(process_queue, queue);
return;
DBG("AVCTP: connected to %s", address);
if (session->control == NULL)
- session->control = avctp_channel_create(session, chan, NULL);
+ session->control = avctp_channel_create(session, chan, 2, NULL);
session->control->imtu = imtu;
session->control->omtu = omtu;
}
avctp_set_state(session, AVCTP_STATE_CONNECTING, 0);
- session->control = avctp_channel_create(session, chan, NULL);
+ session->control = avctp_channel_create(session, chan, 2, NULL);
src = btd_adapter_get_address(device_get_adapter(dev));
dst = device_get_address(dev);
g_free(server);
}
-static struct avctp_pending_req *pending_create(struct avctp_channel *chan,
+static struct avctp_pending_req *pending_create(struct avctp_queue *queue,
avctp_process_cb process,
void *data,
GDestroyNotify destroy)
{
struct avctp_pending_req *p;
- GSList *l, *tmp;
-
- if (!chan->processed)
- goto done;
-
- tmp = g_slist_copy(chan->processed);
-
- /* Find first unused transaction id */
- for (l = tmp; l; l = g_slist_next(l)) {
- struct avctp_pending_req *req = l->data;
-
- if (req->transaction == chan->transaction) {
- chan->transaction++;
- chan->transaction %= 16;
- tmp = g_slist_delete_link(tmp, l);
- l = tmp;
- }
- }
-
- g_slist_free(tmp);
-done:
p = g_new0(struct avctp_pending_req, 1);
- p->chan = chan;
- p->transaction = chan->transaction;
+ p->queue = queue;
+ p->transaction = chan_get_transaction(queue->chan);
p->process = process;
p->data = data;
p->destroy = destroy;
- chan->transaction++;
- chan->transaction %= 16;
-
return p;
}
avctp_rsp_cb func, void *user_data)
{
struct avctp_channel *control = session->control;
+ struct avctp_queue *queue;
struct avctp_pending_req *p;
struct avctp_control_req *req;
if (control == NULL)
return -ENOTCONN;
+ /* If the request set a callback send it directly */
+ if (!func)
+ return avctp_send(session->control, -1, AVCTP_COMMAND,
+ code, subunit, opcode, operands, operand_count);
+
req = g_new0(struct avctp_control_req, 1);
req->code = code;
req->subunit = subunit;
req->operand_count = operand_count;
req->user_data = user_data;
- p = pending_create(control, process_control, req, control_req_destroy);
+ if (opcode == AVC_OP_PASSTHROUGH) {
+ queue = g_slist_nth_data(control->queues, PASSTHROUGH_QUEUE);
+ p = pending_create(queue, process_passthrough, req,
+ control_req_destroy);
+ } else {
+ queue = g_slist_nth_data(control->queues, CONTROL_QUEUE);
+ p = pending_create(queue, process_control, req,
+ control_req_destroy);
+ }
req->p = p;
- g_queue_push_tail(control->queue, p);
+ g_queue_push_tail(queue->queue, p);
- if (control->process_id == 0)
- control->process_id = g_idle_add(process_queue, control);
+ if (queue->process_id == 0)
+ queue->process_id = g_idle_add(process_queue, queue);
return 0;
}
avctp_browsing_rsp_cb func, void *user_data)
{
struct avctp_channel *browsing = session->browsing;
+ struct avctp_queue *queue;
struct avctp_pending_req *p;
struct avctp_browsing_req *req;
req->operand_count = operand_count;
req->user_data = user_data;
- p = pending_create(browsing, process_browsing, req,
- browsing_req_destroy);
+ queue = g_slist_nth_data(browsing->queues, 0);
+
+ p = pending_create(queue, process_browsing, req, browsing_req_destroy);
req->p = p;
- g_queue_push_tail(browsing->queue, p);
+ g_queue_push_tail(queue->queue, p);
/* Connection did not complete, delay process of the request */
if (browsing->watch == 0)
return 0;
- if (browsing->process_id == 0)
- browsing->process_id = g_idle_add(process_queue, browsing);
+ if (queue->process_id == 0)
+ queue->process_id = g_idle_add(process_queue, queue);
return 0;
}
return NULL;
}
- session->control = avctp_channel_create(session, io, NULL);
+ session->control = avctp_channel_create(session, io, 2, NULL);
session->initiator = true;
g_io_channel_unref(io);
return -EIO;
}
- session->browsing = avctp_channel_create(session, io,
+ session->browsing = avctp_channel_create(session, io, 1,
avctp_destroy_browsing);
g_io_channel_unref(io);
#else
#define REQ_TIMEOUT 6
#endif
+#define SUSPEND_TIMEOUT 10
#define ABORT_TIMEOUT 2
#define DISCONNECT_TIMEOUT 1
#define START_TIMEOUT 1
handle_unanswered_req(session, stream);
/* Remove pending commands for this stream from the queue */
cleanup_queue(session, stream);
+ session->streams = g_slist_remove(session->streams, stream);
break;
default:
break;
cb->cb(stream, old_state, state, err_ptr, cb->user_data);
}
- if (state == AVDTP_STATE_IDLE &&
- g_slist_find(session->streams, stream)) {
- session->streams = g_slist_remove(session->streams, stream);
+ if (state == AVDTP_STATE_IDLE)
stream_free(stream);
- }
}
#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
disconnect_timeout,
session);
}
+#else
+ if (!session->stream_setup && !session->streams)
+ session->dc_timer = g_idle_add(disconnect_timeout, session);
+ else
+ session->dc_timer = g_timeout_add_seconds(DISCONNECT_TIMEOUT,
+ disconnect_timeout,
+ session);
#endif
}
}
#else
io = bt_io_connect(avdtp_connect_cb, session,
- NULL, &err,
- BT_IO_OPT_SOURCE_BDADDR, src,
- BT_IO_OPT_DEST_BDADDR,
- device_get_address(session->device),
- BT_IO_OPT_PSM, AVDTP_PSM,
- BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
- BT_IO_OPT_INVALID);
+ NULL, &err,
+ BT_IO_OPT_SOURCE_BDADDR, src,
+ BT_IO_OPT_DEST_BDADDR,
+ device_get_address(session->device),
+ BT_IO_OPT_PSM, AVDTP_PSM,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+ BT_IO_OPT_INVALID);
#endif
if (!io) {
else
stream = NULL;
- if (stream) {
- stream->abort_int = TRUE;
+ if (stream)
lsep = stream->lsep;
- } else
+ else
lsep = NULL;
switch (req->signal_id) {
if (lsep && lsep->cfm && lsep->cfm->set_configuration)
lsep->cfm->set_configuration(session, lsep, stream,
&averr, lsep->user_data);
- goto failed;
+ break;
case AVDTP_DISCOVER:
error("Discover: %s (%d)", strerror(err), err);
goto failed;
goto failed;
}
+ stream->abort_int = TRUE;
+
goto done;
failed:
struct pending_req *req)
{
static int transaction = 0;
- int err;
+ int err, timeout;
if (session->state == AVDTP_SESSION_STATE_DISCONNECTED) {
session->io = l2cap_connect(session);
session->req = req;
- req->timeout = g_timeout_add_seconds(req->signal_id == AVDTP_ABORT ?
- ABORT_TIMEOUT : REQ_TIMEOUT,
- request_timeout,
- session);
+ switch (req->signal_id) {
+ case AVDTP_ABORT:
+ timeout = ABORT_TIMEOUT;
+ break;
+ case AVDTP_SUSPEND:
+ timeout = SUSPEND_TIMEOUT;
+ break;
+ default:
+ timeout = REQ_TIMEOUT;
+ }
+
+ req->timeout = g_timeout_add_seconds(timeout, request_timeout, session);
return 0;
failed:
return avdtp_discover_resp(session, buf, size);
case AVDTP_GET_ALL_CAPABILITIES:
get_all = "ALL_";
+ /* fall through */
case AVDTP_GET_CAPABILITIES:
DBG("GET_%sCAPABILITIES request succeeded", get_all);
if (!avdtp_get_capabilities_resp(session, buf, size))
struct pending_list_items *p;
char *change_path;
+ uint64_t change_uid;
struct avrcp_player_cb *cb;
void *user_data;
else
item = parse_media_folder(session, &operands[i], len);
- if (item) {
- if (g_slist_find(p->items, item))
- goto done;
+ if (item)
p->items = g_slist_append(p->items, item);
- }
i += len;
}
player->change_path = NULL;
}
- media_player_change_folder_complete(mp, player->path, ret);
+ media_player_change_folder_complete(mp, player->path,
+ player->change_uid, ret);
+
+ player->change_uid = 0;
return FALSE;
}
session = player->sessions->data;
set_ct_player(session, player);
player->change_path = g_strdup(path);
+ player->change_uid = uid;
direction = g_str_has_prefix(path, player->path) ? 0x01 : 0x00;
uint8_t *operands, size_t operand_count,
void *user_data)
{
- struct avrcp_browsing_header *pdu = (void *) operands;
+ struct avrcp_header *pdu = (void *) operands;
struct avrcp *session = (void *) user_data;
struct avrcp_player *player = session->controller->player;
struct media_player *mp = player->user_data;
case AVRCP_STATUS_UID_CHANGED:
case AVRCP_STATUS_DOES_NOT_EXIST:
ret = -ENOENT;
+ break;
default:
ret = -EINVAL;
+ break;
}
goto done;
}
if (!session->controller ||
!session->controller->player)
break;
+ /* fall through */
case AVRCP_EVENT_VOLUME_CHANGED:
#endif
avrcp_register_notification(session, event);
{
struct control *control = btd_service_get_user_data(service);
- if (!control->session)
+ if (!control || !control->session)
return -ENOTCONN;
avctp_disconnect(control->session);
avctp_remove_state_cb(control->avctp_id);
- if (control->target)
+ if (control->target) {
+ btd_service_set_user_data(control->target, NULL);
btd_service_unref(control->target);
+ }
- if (control->remote)
+ if (control->remote) {
+ btd_service_set_user_data(control->remote, NULL);
btd_service_unref(control->remote);
+ }
devices = g_slist_remove(devices, control);
g_free(control);
dbus_message_iter_close_container(array, &entry);
}
-void media_player_change_folder_complete(struct media_player *mp,
- const char *path, int ret)
-{
- struct media_folder *folder = mp->scope;
- DBusMessage *reply;
-
- if (folder == NULL || folder->msg == NULL)
- return;
-
- if (ret < 0) {
- reply = btd_error_failed(folder->msg, strerror(-ret));
- goto done;
- }
-
- media_player_set_folder(mp, path, ret);
-
- reply = g_dbus_create_reply(folder->msg, DBUS_TYPE_INVALID);
-
-done:
- g_dbus_send_message(btd_get_dbus_connection(), reply);
- dbus_message_unref(folder->msg);
- folder->msg = NULL;
-}
-
void media_player_list_complete(struct media_player *mp, GSList *items,
int err)
{
return media_player_change_scope(mp, folder);
}
+static struct media_folder *
+media_player_find_folder_by_uid(struct media_player *mp, uint64_t uid)
+{
+ struct media_folder *folder = mp->scope;
+ struct media_folder *parent = folder->parent;
+ GSList *l;
+
+ if (parent && parent->item->uid == uid)
+ return parent;
+
+ for (l = folder->subfolders; l; l = l->next) {
+ struct media_folder *folder = l->data;
+
+ if (folder->item->uid == uid)
+ return folder;
+ }
+
+ return NULL;
+}
+
+static void media_player_set_folder_by_uid(struct media_player *mp,
+ uint64_t uid, uint32_t number_of_items)
+{
+ struct media_folder *folder;
+
+ DBG("uid %" PRIu64 " number of items %u", uid, number_of_items);
+
+ folder = media_player_find_folder_by_uid(mp, uid);
+ if (folder == NULL) {
+ error("Unknown folder: %" PRIu64, uid);
+ return;
+ }
+
+ folder->number_of_items = number_of_items;
+
+ media_player_set_scope(mp, folder);
+}
+
+void media_player_change_folder_complete(struct media_player *mp,
+ const char *path, uint64_t uid,
+ int ret)
+{
+ struct media_folder *folder = mp->scope;
+ DBusMessage *reply;
+
+ if (folder == NULL || folder->msg == NULL)
+ return;
+
+ if (ret < 0) {
+ reply = btd_error_failed(folder->msg, strerror(-ret));
+ goto done;
+ }
+
+ media_player_set_folder_by_uid(mp, uid, ret);
+
+ reply = g_dbus_create_reply(folder->msg, DBUS_TYPE_INVALID);
+
+done:
+ g_dbus_send_message(btd_get_dbus_connection(), reply);
+ dbus_message_unref(folder->msg);
+ folder->msg = NULL;
+}
+
void media_player_destroy(struct media_player *mp)
{
DBG("%s", mp->path);
}
static const GDBusMethodTable media_item_methods[] = {
- { GDBUS_METHOD("Play", NULL, NULL, media_item_play) },
+ { GDBUS_ASYNC_METHOD("Play", NULL, NULL, media_item_play) },
{ GDBUS_METHOD("AddtoNowPlaying", NULL, NULL,
media_item_add_to_nowplaying) },
{ }
return media_folder_create_item(mp, mp->scope, name, type, uid);
}
+#if 0
static struct media_folder *
media_player_find_folder_by_uid(struct media_player *mp, uint64_t uid)
{
return NULL;
}
+#endif
struct media_item *media_player_create_folder(struct media_player *mp,
const char *name,
void media_player_list_complete(struct media_player *mp, GSList *items,
int err);
void media_player_change_folder_complete(struct media_player *player,
- const char *path, int ret);
+ const char *path, uint64_t uid,
+ int ret);
void media_player_search_complete(struct media_player *mp, int ret);
void media_player_total_items_complete(struct media_player *mp,
uint32_t num_of_items);
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2012 Instituto Nokia de Tecnologia - INdT
+ * Copyright (C) 2014 Google Inc.
+ * Copyright (C) 2017 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <ctype.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include "gdbus/gdbus.h"
+
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/sdp.h"
+#include "lib/uuid.h"
+
+#include "src/dbus-common.h"
+#include "src/shared/util.h"
+#include "src/shared/att.h"
+#include "src/shared/queue.h"
+#include "src/shared/gatt-db.h"
+#include "src/shared/gatt-client.h"
+#include "src/plugin.h"
+#include "src/adapter.h"
+#include "src/device.h"
+#include "src/profile.h"
+#include "src/service.h"
+#include "src/log.h"
+#include "attrib/att.h"
+
+#define BATTERY_INTERFACE "org.bluez.Battery1"
+
+#define BATT_UUID16 0x180f
+
+/* Generic Attribute/Access Service */
+struct batt {
+ char *path; /* D-Bus path of device */
+ struct btd_device *device;
+ struct gatt_db *db;
+ struct bt_gatt_client *client;
+ struct gatt_db_attribute *attr;
+
+ unsigned int batt_level_cb_id;
+ uint16_t batt_level_io_handle;
+
+ uint8_t *initial_value;
+ uint8_t percentage;
+};
+
+static void batt_free(struct batt *batt)
+{
+ gatt_db_unref(batt->db);
+ bt_gatt_client_unref(batt->client);
+ btd_device_unref(batt->device);
+ g_free (batt->initial_value);
+ g_free(batt);
+}
+
+static void batt_reset(struct batt *batt)
+{
+ batt->attr = NULL;
+ gatt_db_unref(batt->db);
+ batt->db = NULL;
+ bt_gatt_client_unref(batt->client);
+ batt->client = NULL;
+ g_free (batt->initial_value);
+ batt->initial_value = NULL;
+ if (batt->path) {
+ g_dbus_unregister_interface(btd_get_dbus_connection(),
+ batt->path, BATTERY_INTERFACE);
+ g_free(batt->path);
+ batt->path = NULL;
+ }
+}
+
+static void parse_battery_level(struct batt *batt,
+ const uint8_t *value)
+{
+ uint8_t percentage;
+
+ percentage = value[0];
+ if (batt->percentage != percentage) {
+ batt->percentage = percentage;
+ DBG("Battery Level updated: %d%%", percentage);
+ g_dbus_emit_property_changed(btd_get_dbus_connection(), batt->path,
+ BATTERY_INTERFACE, "Percentage");
+ }
+}
+
+static void batt_io_value_cb(uint16_t value_handle, const uint8_t *value,
+ uint16_t length, void *user_data)
+{
+ struct batt *batt = user_data;
+
+ if (value_handle == batt->batt_level_io_handle) {
+ parse_battery_level(batt, value);
+ } else {
+ g_assert_not_reached();
+ }
+}
+
+static gboolean property_get_percentage(
+ const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct batt *batt = data;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_BYTE, &batt->percentage);
+
+ return TRUE;
+}
+
+static const GDBusPropertyTable battery_properties[] = {
+ { "Percentage", "y", property_get_percentage },
+ { }
+};
+
+static void batt_io_ccc_written_cb(uint16_t att_ecode, void *user_data)
+{
+ struct batt *batt = user_data;
+
+ if (att_ecode != 0) {
+ error("Battery Level: notifications not enabled %s",
+ att_ecode2str(att_ecode));
+ return;
+ }
+
+ if (g_dbus_register_interface(btd_get_dbus_connection(),
+ batt->path, BATTERY_INTERFACE,
+ NULL, NULL,
+ battery_properties, batt,
+ NULL) == FALSE) {
+ error("Unable to register %s interface for %s",
+ BATTERY_INTERFACE, batt->path);
+ batt_reset(batt);
+ return;
+ }
+
+ parse_battery_level(batt, batt->initial_value);
+ g_free (batt->initial_value);
+ batt->initial_value = NULL;
+
+ DBG("Battery Level: notification enabled");
+}
+
+static void read_initial_battery_level_cb(bool success,
+ uint8_t att_ecode,
+ const uint8_t *value,
+ uint16_t length,
+ void *user_data)
+{
+ struct batt *batt = user_data;
+
+ if (!success) {
+ DBG("Reading battery level failed with ATT errror: %u",
+ att_ecode);
+ return;
+ }
+
+ if (!length)
+ return;
+
+ batt->initial_value = g_memdup(value, length);
+
+ /* request notify */
+ batt->batt_level_cb_id =
+ bt_gatt_client_register_notify(batt->client,
+ batt->batt_level_io_handle,
+ batt_io_ccc_written_cb,
+ batt_io_value_cb,
+ batt,
+ NULL);
+}
+
+static void handle_battery_level(struct batt *batt, uint16_t value_handle)
+{
+ batt->batt_level_io_handle = value_handle;
+
+ if (!bt_gatt_client_read_value(batt->client, batt->batt_level_io_handle,
+ read_initial_battery_level_cb, batt, NULL))
+ DBG("Failed to send request to read battery level");
+}
+
+static bool uuid_cmp(uint16_t u16, const bt_uuid_t *uuid)
+{
+ bt_uuid_t lhs;
+
+ bt_uuid16_create(&lhs, u16);
+
+ return bt_uuid_cmp(&lhs, uuid) == 0;
+}
+
+static void handle_characteristic(struct gatt_db_attribute *attr,
+ void *user_data)
+{
+ struct batt *batt = user_data;
+ uint16_t value_handle;
+ bt_uuid_t uuid;
+
+ if (!gatt_db_attribute_get_char_data(attr, NULL, &value_handle, NULL,
+ NULL, &uuid)) {
+ error("Failed to obtain characteristic data");
+ return;
+ }
+
+ if (uuid_cmp(GATT_CHARAC_BATTERY_LEVEL, &uuid)) {
+ handle_battery_level(batt, value_handle);
+ } else {
+ char uuid_str[MAX_LEN_UUID_STR];
+
+ bt_uuid_to_string(&uuid, uuid_str, sizeof(uuid_str));
+ DBG("Unsupported characteristic: %s", uuid_str);
+ }
+}
+
+static void handle_batt_service(struct batt *batt)
+{
+ gatt_db_service_foreach_char(batt->attr, handle_characteristic, batt);
+}
+
+static int batt_probe(struct btd_service *service)
+{
+ struct btd_device *device = btd_service_get_device(service);
+ struct batt *batt = btd_service_get_user_data(service);
+ char addr[18];
+
+ ba2str(device_get_address(device), addr);
+ DBG("BATT profile probe (%s)", addr);
+
+ /* Ignore, if we were probed for this device already */
+ if (batt) {
+ error("Profile probed twice for the same device!");
+ return -1;
+ }
+
+ batt = g_new0(struct batt, 1);
+ if (!batt)
+ return -1;
+
+ batt->percentage = -1;
+ batt->device = btd_device_ref(device);
+ btd_service_set_user_data(service, batt);
+
+ return 0;
+}
+
+static void batt_remove(struct btd_service *service)
+{
+ struct btd_device *device = btd_service_get_device(service);
+ struct batt *batt;
+ char addr[18];
+
+ ba2str(device_get_address(device), addr);
+ DBG("BATT profile remove (%s)", addr);
+
+ batt = btd_service_get_user_data(service);
+ if (!batt) {
+ error("BATT service not handled by profile");
+ return;
+ }
+
+ batt_free(batt);
+}
+
+static void foreach_batt_service(struct gatt_db_attribute *attr, void *user_data)
+{
+ struct batt *batt = user_data;
+
+ if (batt->attr) {
+ error("More than one BATT service exists for this device");
+ return;
+ }
+
+ batt->attr = attr;
+ handle_batt_service(batt);
+}
+
+static int batt_accept(struct btd_service *service)
+{
+ struct btd_device *device = btd_service_get_device(service);
+ struct gatt_db *db = btd_device_get_gatt_db(device);
+ struct bt_gatt_client *client = btd_device_get_gatt_client(device);
+ struct batt *batt = btd_service_get_user_data(service);
+ char addr[18];
+ bt_uuid_t batt_uuid;
+
+ ba2str(device_get_address(device), addr);
+ DBG("BATT profile accept (%s)", addr);
+
+ if (!batt) {
+ error("BATT service not handled by profile");
+ return -1;
+ }
+
+ batt->db = gatt_db_ref(db);
+ batt->client = bt_gatt_client_clone(client);
+
+ /* Handle the BATT services */
+ bt_uuid16_create(&batt_uuid, BATT_UUID16);
+ gatt_db_foreach_service(db, &batt_uuid, foreach_batt_service, batt);
+
+ if (!batt->attr) {
+ error("BATT attribute not found");
+ batt_reset(batt);
+ return -1;
+ }
+
+ batt->path = g_strdup (device_get_path(device));
+
+ btd_service_connecting_complete(service, 0);
+
+ return 0;
+}
+
+static int batt_disconnect(struct btd_service *service)
+{
+ struct batt *batt = btd_service_get_user_data(service);
+
+ batt_reset(batt);
+
+ btd_service_disconnecting_complete(service, 0);
+
+ return 0;
+}
+
+static struct btd_profile batt_profile = {
+ .name = "batt-profile",
+ .remote_uuid = BATTERY_UUID,
+ .device_probe = batt_probe,
+ .device_remove = batt_remove,
+ .accept = batt_accept,
+ .disconnect = batt_disconnect,
+};
+
+static int batt_init(void)
+{
+ return btd_profile_register(&batt_profile);
+}
+
+static void batt_exit(void)
+{
+ btd_profile_unregister(&batt_profile);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(battery, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
+ batt_init, batt_exit)
#include "src/shared/util.h"
#include "src/shared/queue.h"
+#include "src/shared/att.h"
+#include "src/shared/gatt-db.h"
#include "attrib/gattrib.h"
#include "attrib/att.h"
#include "profiles/deviceinfo/dis.h"
+#define DIS_UUID16 0x180a
#define PNP_ID_SIZE 7
struct bt_dis {
g_free(dis);
}
-struct bt_dis *bt_dis_new(void *primary)
+static void foreach_dis_char(struct gatt_db_attribute *attr, void *user_data)
+{
+ struct bt_dis *dis = user_data;
+ bt_uuid_t pnpid_uuid, uuid;
+ uint16_t value_handle;
+
+ /* Ignore if there are multiple instances */
+ if (dis->handle)
+ return;
+
+ if (!gatt_db_attribute_get_char_data(attr, NULL, &value_handle, NULL, NULL, &uuid))
+ return;
+
+ /* Find PNPID characteristic's value handle */
+ bt_string_to_uuid(&pnpid_uuid, PNPID_UUID);
+ if (bt_uuid_cmp(&pnpid_uuid, &uuid) == 0)
+ dis->handle = value_handle;
+}
+
+static void foreach_dis_service(struct gatt_db_attribute *attr, void *user_data)
+{
+ struct bt_dis *dis = user_data;
+
+ /* Ignore if there are multiple instances */
+ if (dis->handle)
+ return;
+
+ gatt_db_service_foreach_char(attr, foreach_dis_char, dis);
+}
+
+struct bt_dis *bt_dis_new(struct gatt_db *db)
+{
+ struct bt_dis *dis;
+
+ dis = g_try_new0(struct bt_dis, 1);
+ if (!dis)
+ return NULL;
+
+ dis->gatt_op = queue_new();
+
+ if (db) {
+ bt_uuid_t uuid;
+
+ /* Handle the DIS service */
+ bt_uuid16_create(&uuid, DIS_UUID16);
+ gatt_db_foreach_service(db, &uuid, foreach_dis_service, dis);
+ if (!dis->handle) {
+ dis_free(dis);
+ return NULL;
+ }
+ }
+
+ return bt_dis_ref(dis);
+}
+
+struct bt_dis *bt_dis_new_primary(void *primary)
{
struct bt_dis *dis;
{
struct gatt_primary *primary = dis->primary;
- if (dis->attrib || !primary)
+ if (dis->attrib)
return false;
dis->attrib = g_attrib_ref(attrib);
discover_char(dis, dis->attrib, primary->range.start,
primary->range.end, NULL,
configure_deviceinfo_cb, dis);
+ else
+ read_char(dis, attrib, dis->handle, read_pnpid_cb, dis);
return true;
}
struct bt_dis;
-struct bt_dis *bt_dis_new(void *primary);
+struct bt_dis *bt_dis_new(struct gatt_db *db);
+struct bt_dis *bt_dis_new_primary(void *primary);
struct bt_dis *bt_dis_ref(struct bt_dis *dis);
void bt_dis_unref(struct bt_dis *dis);
}
gas->db = gatt_db_ref(db);
- gas->client = bt_gatt_client_ref(client);
+ gas->client = bt_gatt_client_clone(client);
/* Handle the GAP services */
bt_uuid16_create(&gap_uuid, GAP_UUID16);
*/
if (device_is_temporary(idev->device) ||
btd_device_is_connected(idev->device))
- return FALSE;
+ goto bail;
/* Only attempt an auto-reconnect for at most 3 minutes (6 * 30s). */
if (idev->reconnect_attempt >= 6)
- return FALSE;
+ goto bail;
/* Check if the profile is already connected. */
if (idev->ctrl_io)
- return FALSE;
+ goto bail;
if (is_connected(idev))
- return FALSE;
+ goto bail;
idev->reconnect_attempt++;
dev_connect(idev);
return TRUE;
+
+bail:
+ idev->reconnect_timer = 0;
+ return FALSE;
}
static const char * const _reconnect_mode_str[] = {
#include "src/shared/util.h"
#include "src/shared/uhid.h"
#include "src/shared/queue.h"
+#include "src/shared/att.h"
+#include "src/shared/gatt-db.h"
#include "src/log.h"
#include "attrib/att.h"
#include "profiles/input/hog-lib.h"
#define HOG_UUID "00001812-0000-1000-8000-00805f9b34fb"
+#define HOG_UUID16 0x1812
#define HOG_INFO_UUID 0x2A4A
#define HOG_REPORT_MAP_UUID 0x2A4B
uint16_t vendor;
uint16_t product;
uint16_t version;
+ struct gatt_db_attribute *attr;
struct gatt_primary *primary;
GAttrib *attrib;
GSList *reports;
struct bt_hog *hog;
uint8_t id;
uint8_t type;
+ uint16_t handle;
+ uint16_t value_handle;
+ uint8_t properties;
uint16_t ccc_handle;
guint notifyid;
- struct gatt_char *decl;
uint16_t len;
uint8_t *value;
};
struct gatt_request *req;
unsigned int id;
+ /* Ignore if not connected */
+ if (!attrib)
+ return;
+
req = create_request(hog, user_data);
if (!req)
return;
report->notifyid = g_attrib_register(hog->attrib,
ATT_OP_HANDLE_NOTIFY,
- report->decl->value_handle,
+ report->value_handle,
report_value_cb, report, NULL);
DBG("Report characteristic descriptor written: notifications enabled");
report->id = pdu[1];
report->type = pdu[2];
- DBG("Report 0x%04x: id 0x%02x type %s", report->decl->value_handle,
+ DBG("Report 0x%04x: id 0x%02x type %s", report->value_handle,
report->id, type_to_string(report->type));
/* Enable notifications only for Input Reports */
const struct report *report = data;
const struct gatt_char *decl = user_data;
- return report->decl->handle - decl->handle;
+ return report->handle - decl->handle;
}
static struct report *report_new(struct bt_hog *hog, struct gatt_char *chr)
report = g_new0(struct report, 1);
report->hog = hog;
- report->decl = g_memdup(chr, sizeof(*chr));
+ report->handle = chr->handle;
+ report->value_handle = chr->value_handle;
+ report->properties = chr->properties;
hog->reports = g_slist_append(hog->reports, report);
read_char(hog, hog->attrib, chr->value_handle, report_read_cb, report);
}
DBG("Sending report type %d ID %d to handle 0x%X", report->type,
- report->id, report->decl->value_handle);
+ report->id, report->value_handle);
if (hog->attrib == NULL)
return;
- if (report->decl->properties & GATT_CHR_PROP_WRITE)
- write_char(hog, hog->attrib, report->decl->value_handle,
+ if (report->properties & GATT_CHR_PROP_WRITE)
+ write_char(hog, hog->attrib, report->value_handle,
data, size, output_written_cb, hog);
- else if (report->decl->properties & GATT_CHR_PROP_WRITE_WITHOUT_RESP)
- gatt_write_cmd(hog->attrib, report->decl->value_handle,
+ else if (report->properties & GATT_CHR_PROP_WRITE_WITHOUT_RESP)
+ gatt_write_cmd(hog->attrib, report->value_handle,
data, size, NULL, NULL);
}
-static void get_feature(struct uhid_event *ev, void *user_data)
-{
- struct bt_hog *hog = user_data;
- struct report *report;
- struct uhid_event rsp;
- int err;
-
- memset(&rsp, 0, sizeof(rsp));
- rsp.type = UHID_FEATURE_ANSWER;
- rsp.u.feature_answer.id = ev->u.feature.id;
-
- report = find_report_by_rtype(hog, ev->u.feature.rtype,
- ev->u.feature.rnum);
- if (!report) {
- rsp.u.feature_answer.err = ENOTSUP;
- goto done;
- }
-
- if (!report->value) {
- rsp.u.feature_answer.err = EIO;
- goto done;
- }
-
- rsp.u.feature_answer.size = report->len;
- memcpy(rsp.u.feature_answer.data, report->value, report->len);
-
-done:
- err = bt_uhid_send(hog->uhid, &rsp);
- if (err < 0)
- error("bt_uhid_send: %s", strerror(-err));
-}
-
static void set_report_cb(guint8 status, const guint8 *pdu,
guint16 plen, gpointer user_data)
{
}
DBG("Sending report type %d ID %d to handle 0x%X", report->type,
- report->id, report->decl->value_handle);
+ report->id, report->value_handle);
if (hog->attrib == NULL)
return;
hog->setrep_att = gatt_write_char(hog->attrib,
- report->decl->value_handle,
+ report->value_handle,
data, size, set_report_cb,
hog);
if (!hog->setrep_att) {
}
hog->getrep_att = gatt_read_char(hog->attrib,
- report->decl->value_handle,
+ report->value_handle,
get_report_cb, hog);
if (!hog->getrep_att) {
err = ENOMEM;
}
bt_uhid_register(hog->uhid, UHID_OUTPUT, forward_report, hog);
- bt_uhid_register(hog->uhid, UHID_FEATURE, get_feature, hog);
bt_uhid_register(hog->uhid, UHID_GET_REPORT, get_report, hog);
bt_uhid_register(hog->uhid, UHID_SET_REPORT, set_report, hog);
struct report *report = data;
g_free(report->value);
- g_free(report->decl);
g_free(report);
}
struct bt_hog *bt_hog_new_default(const char *name, uint16_t vendor,
uint16_t product, uint16_t version,
- void *primary)
+ struct gatt_db *db)
{
- return bt_hog_new(-1, name, vendor, product, version, primary);
+ return bt_hog_new(-1, name, vendor, product, version, db);
}
-struct bt_hog *bt_hog_new(int fd, const char *name, uint16_t vendor,
+static void foreach_hog_report(struct gatt_db_attribute *attr, void *user_data)
+{
+ struct report *report = user_data;
+ struct bt_hog *hog = report->hog;
+ const bt_uuid_t *uuid;
+ bt_uuid_t ref_uuid, ccc_uuid;
+ uint16_t handle;
+
+ handle = gatt_db_attribute_get_handle(attr);
+ uuid = gatt_db_attribute_get_type(attr);
+
+ bt_uuid16_create(&ref_uuid, GATT_REPORT_REFERENCE);
+ if (!bt_uuid_cmp(&ref_uuid, uuid)) {
+ read_char(hog, hog->attrib, handle, report_reference_cb,
+ report);
+ return;
+ }
+
+ bt_uuid16_create(&ccc_uuid, GATT_CLIENT_CHARAC_CFG_UUID);
+ if (!bt_uuid_cmp(&ccc_uuid, uuid))
+ report->ccc_handle = handle;
+}
+
+static int report_attr_cmp(const void *data, const void *user_data)
+{
+ const struct report *report = data;
+ const struct gatt_db_attribute *attr = user_data;
+
+ return report->handle - gatt_db_attribute_get_handle(attr);
+}
+
+static struct report *report_add(struct bt_hog *hog,
+ struct gatt_db_attribute *attr)
+{
+ struct report *report;
+ GSList *l;
+
+ /* Skip if report already exists */
+ l = g_slist_find_custom(hog->reports, attr, report_attr_cmp);
+ if (l)
+ return l->data;
+
+ report = g_new0(struct report, 1);
+ report->hog = hog;
+
+ gatt_db_attribute_get_char_data(attr, &report->handle,
+ &report->value_handle,
+ &report->properties,
+ NULL, NULL);
+
+ hog->reports = g_slist_append(hog->reports, report);
+
+ read_char(hog, hog->attrib, report->value_handle, report_read_cb,
+ report);
+
+ return report;
+}
+
+static void foreach_hog_external(struct gatt_db_attribute *attr,
+ void *user_data)
+{
+ struct bt_hog *hog = user_data;
+ const bt_uuid_t *uuid;
+ bt_uuid_t ext_uuid;
+ uint16_t handle;
+
+ handle = gatt_db_attribute_get_handle(attr);
+ uuid = gatt_db_attribute_get_type(attr);
+
+ bt_uuid16_create(&ext_uuid, GATT_EXTERNAL_REPORT_REFERENCE);
+ if (!bt_uuid_cmp(&ext_uuid, uuid))
+ read_char(hog, hog->attrib, handle,
+ external_report_reference_cb, hog);
+}
+
+static void foreach_hog_chrc(struct gatt_db_attribute *attr, void *user_data)
+{
+ struct bt_hog *hog = user_data;
+ bt_uuid_t uuid, report_uuid, report_map_uuid, info_uuid;
+ bt_uuid_t proto_mode_uuid, ctrlpt_uuid;
+ uint16_t handle, value_handle;
+
+ gatt_db_attribute_get_char_data(attr, &handle, &value_handle, NULL,
+ NULL, &uuid);
+
+ bt_uuid16_create(&report_uuid, HOG_REPORT_UUID);
+ if (!bt_uuid_cmp(&report_uuid, &uuid)) {
+ struct report *report = report_add(hog, attr);
+ gatt_db_service_foreach_desc(attr, foreach_hog_report, report);
+ return;
+ }
+
+ bt_uuid16_create(&report_map_uuid, HOG_REPORT_MAP_UUID);
+ if (!bt_uuid_cmp(&report_map_uuid, &uuid)) {
+ read_char(hog, hog->attrib, value_handle, report_map_read_cb,
+ hog);
+ gatt_db_service_foreach_desc(attr, foreach_hog_external, hog);
+ return;
+ }
+
+ bt_uuid16_create(&info_uuid, HOG_INFO_UUID);
+ if (!bt_uuid_cmp(&info_uuid, &uuid)) {
+ read_char(hog, hog->attrib, value_handle, info_read_cb, hog);
+ return;
+ }
+
+ bt_uuid16_create(&proto_mode_uuid, HOG_PROTO_MODE_UUID);
+ if (!bt_uuid_cmp(&proto_mode_uuid, &uuid)) {
+ hog->proto_mode_handle = value_handle;
+ read_char(hog, hog->attrib, value_handle, proto_mode_read_cb,
+ hog);
+ }
+
+ bt_uuid16_create(&ctrlpt_uuid, HOG_CONTROL_POINT_UUID);
+ if (!bt_uuid_cmp(&ctrlpt_uuid, &uuid))
+ hog->ctrlpt_handle = value_handle;
+}
+
+static struct bt_hog *hog_new(int fd, const char *name, uint16_t vendor,
uint16_t product, uint16_t version,
- void *primary)
+ struct gatt_db_attribute *attr)
{
struct bt_hog *hog;
hog->vendor = vendor;
hog->product = product;
hog->version = version;
+ hog->attr = attr;
+
+ return hog;
+}
+
+static void hog_attach_instace(struct bt_hog *hog,
+ struct gatt_db_attribute *attr)
+{
+ struct bt_hog *instance;
+
+ if (!hog->attr) {
+ hog->attr = attr;
+ gatt_db_service_foreach_char(hog->attr, foreach_hog_chrc, hog);
+ return;
+ }
+
+ instance = hog_new(hog->uhid_fd, hog->name, hog->vendor,
+ hog->product, hog->version, attr);
+ if (!instance)
+ return;
+
+ hog->instances = g_slist_append(hog->instances, instance);
+}
- if (primary)
- hog->primary = g_memdup(primary, sizeof(*hog->primary));
+static void foreach_hog_service(struct gatt_db_attribute *attr, void *user_data)
+{
+ struct bt_hog *hog = user_data;
+
+ hog_attach_instace(hog, attr);
+}
+
+static void dis_notify(uint8_t source, uint16_t vendor, uint16_t product,
+ uint16_t version, void *user_data)
+{
+ struct bt_hog *hog = user_data;
+
+ hog->vendor = vendor;
+ hog->product = product;
+ hog->version = version;
+}
+
+struct bt_hog *bt_hog_new(int fd, const char *name, uint16_t vendor,
+ uint16_t product, uint16_t version,
+ struct gatt_db *db)
+{
+ struct bt_hog *hog;
+
+ hog = hog_new(fd, name, vendor, product, version, NULL);
+ if (!hog)
+ return NULL;
+
+ if (db) {
+ bt_uuid_t uuid;
+
+ /* Handle the HID services */
+ bt_uuid16_create(&uuid, HOG_UUID16);
+ gatt_db_foreach_service(db, &uuid, foreach_hog_service, hog);
+ if (!hog->attr) {
+ hog_free(hog);
+ return NULL;
+ }
+
+ /* Try creating a DIS instance in case pid/vid are not set */
+ if (!vendor && !product) {
+ hog->dis = bt_dis_new(db);
+ bt_dis_set_notification(hog->dis, dis_notify, hog);
+ }
+ }
return bt_hog_ref(hog);
}
bt_scpp_attach(hog->scpp, hog->attrib);
}
-static void dis_notify(uint8_t source, uint16_t vendor, uint16_t product,
- uint16_t version, void *user_data)
-{
- struct bt_hog *hog = user_data;
-
- hog->vendor = vendor;
- hog->product = product;
- hog->version = version;
-}
-
static void hog_attach_dis(struct bt_hog *hog, struct gatt_primary *primary)
{
if (hog->dis) {
return;
}
- hog->dis = bt_dis_new(primary);
+ hog->dis = bt_dis_new_primary(primary);
if (hog->dis) {
bt_dis_set_notification(hog->dis, dis_notify, hog);
bt_dis_attach(hog->dis, hog->attrib);
}
instance = bt_hog_new(hog->uhid_fd, hog->name, hog->vendor,
- hog->product, hog->version, primary);
+ hog->product, hog->version, NULL);
if (!instance)
return;
+ instance->primary = g_memdup(primary, sizeof(*primary));
find_included(instance, hog->attrib, primary->range.start,
primary->range.end, find_included_cb, instance);
bool bt_hog_attach(struct bt_hog *hog, void *gatt)
{
- struct gatt_primary *primary = hog->primary;
GSList *l;
if (hog->attrib)
hog->attrib = g_attrib_ref(gatt);
- if (!primary) {
+ if (!hog->attr && !hog->primary) {
discover_primary(hog, hog->attrib, NULL, primary_cb, hog);
return true;
}
if (!hog->uhid_created) {
DBG("HoG discovering characteristics");
- discover_char(hog, hog->attrib, primary->range.start,
- primary->range.end, NULL,
- char_discovered_cb, hog);
+ if (hog->attr)
+ gatt_db_service_foreach_char(hog->attr,
+ foreach_hog_chrc, hog);
+ else
+ discover_char(hog, hog->attrib,
+ hog->primary->range.start,
+ hog->primary->range.end, NULL,
+ char_discovered_cb, hog);
return true;
}
r->notifyid = g_attrib_register(hog->attrib,
ATT_OP_HANDLE_NOTIFY,
- r->decl->value_handle,
+ r->value_handle,
report_value_cb, r, NULL);
}
if (!report)
return -ENOTSUP;
- DBG("hog: Write report, handle 0x%X", report->decl->value_handle);
+ DBG("hog: Write report, handle 0x%X", report->value_handle);
- if (report->decl->properties & GATT_CHR_PROP_WRITE)
- write_char(hog, hog->attrib, report->decl->value_handle,
+ if (report->properties & GATT_CHR_PROP_WRITE)
+ write_char(hog, hog->attrib, report->value_handle,
data, size, output_written_cb, hog);
- if (report->decl->properties & GATT_CHR_PROP_WRITE_WITHOUT_RESP)
- gatt_write_cmd(hog->attrib, report->decl->value_handle,
+ if (report->properties & GATT_CHR_PROP_WRITE_WITHOUT_RESP)
+ gatt_write_cmd(hog->attrib, report->value_handle,
data, size, NULL, NULL);
for (l = hog->instances; l; l = l->next) {
struct bt_hog *bt_hog_new_default(const char *name, uint16_t vendor,
uint16_t product, uint16_t version,
- void *primary);
+ struct gatt_db *db);
struct bt_hog *bt_hog_new(int fd, const char *name, uint16_t vendor,
uint16_t product, uint16_t version,
- void *primary);
+ struct gatt_db *db);
struct bt_hog *bt_hog_ref(struct bt_hog *hog);
void bt_hog_unref(struct bt_hog *hog);
#define HOG_UUID "00001812-0000-1000-8000-00805f9b34fb"
struct hog_device {
- guint attioid;
struct btd_device *device;
struct bt_hog *hog;
};
static gboolean suspend_supported = FALSE;
static struct queue *devices = NULL;
-static struct hog_device *hog_device_new(struct btd_device *device,
- struct gatt_primary *prim)
+static void hog_device_accept(struct hog_device *dev, struct gatt_db *db)
{
- struct hog_device *dev;
char name[248];
uint16_t vendor, product, version;
- if (device_name_known(device))
- device_get_name(device, name, sizeof(name));
+ if (dev->hog)
+ return;
+
+ if (device_name_known(dev->device))
+ device_get_name(dev->device, name, sizeof(name));
else
strcpy(name, "bluez-hog-device");
- vendor = btd_device_get_vendor(device);
- product = btd_device_get_product(device);
- version = btd_device_get_version(device);
+ vendor = btd_device_get_vendor(dev->device);
+ product = btd_device_get_product(dev->device);
+ version = btd_device_get_version(dev->device);
DBG("name=%s vendor=0x%X, product=0x%X, version=0x%X", name, vendor,
product, version);
- dev = new0(struct hog_device, 1);
- dev->hog = bt_hog_new_default(name, vendor, product, version, prim);
- if (!dev->hog) {
- free(dev);
- return NULL;
- }
+ dev->hog = bt_hog_new_default(name, vendor, product, version, db);
+}
+static struct hog_device *hog_device_new(struct btd_device *device)
+{
+ struct hog_device *dev;
+
+ dev = new0(struct hog_device, 1);
dev->device = btd_device_ref(device);
if (!devices)
{
struct btd_device *device = btd_service_get_device(service);
const char *path = device_get_path(device);
- GSList *primaries, *l;
+ struct hog_device *dev;
DBG("path %s", path);
- primaries = btd_device_get_primaries(device);
- if (primaries == NULL)
+ dev = hog_device_new(device);
+ if (!dev)
return -EINVAL;
- for (l = primaries; l; l = g_slist_next(l)) {
- struct gatt_primary *prim = l->data;
- struct hog_device *dev;
-
- if (strcmp(prim->uuid, HOG_UUID) != 0)
- continue;
-
- dev = hog_device_new(device, prim);
- if (!dev)
- break;
-
- btd_service_set_user_data(service, dev);
- return 0;
- }
-
- return -EINVAL;
+ btd_service_set_user_data(service, dev);
+ return 0;
}
static void hog_remove(struct btd_service *service)
{
struct hog_device *dev = btd_service_get_user_data(service);
struct btd_device *device = btd_service_get_device(service);
+ struct gatt_db *db = btd_device_get_gatt_db(device);
GAttrib *attrib = btd_device_get_attrib(device);
+ if (!dev->hog) {
+ hog_device_accept(dev, db);
+ if (!dev->hog)
+ return -EINVAL;
+ }
+
/* TODO: Replace GAttrib with bt_gatt_client */
bt_hog_attach(dev->hog, attrib);
#include "src/device.h"
#include "src/profile.h"
+#include "sixaxis.h"
#include "device.h"
#include "server.h"
{
struct btd_device *device;
uint16_t vid, pid;
+ const struct cable_pairing *cp;
device = btd_adapter_find_device(adapter_find(src), dst, BDADDR_BREDR);
if (!device)
vid = btd_device_get_vendor(device);
pid = btd_device_get_product(device);
- /* DualShock 3 */
- if (vid == 0x054c && pid == 0x0268)
- return true;
-
- /* DualShock 4 */
- if (vid == 0x054c && pid == 0x05c4)
- return true;
-
- /* Navigation Controller */
- if (vid == 0x054c && pid == 0x042f)
+ cp = get_pairing(vid, pid);
+ if (cp && (cp->type == CABLE_PAIRING_SIXAXIS ||
+ cp->type == CABLE_PAIRING_DS4))
return true;
return false;
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2009,2017 Bastien Nocera <hadess@hadess.net>
+ * Copyright (C) 2011 Antonio Ospite <ospite@studenti.unina.it>
+ * Copyright (C) 2013 Szymon Janc <szymon.janc@gmail.com>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifndef _SIXAXIS_H_
+#define _SIXAXIS_H_
+
+typedef enum {
+ CABLE_PAIRING_UNSUPPORTED = 0,
+ CABLE_PAIRING_SIXAXIS,
+ CABLE_PAIRING_DS4,
+} CablePairingType;
+
+struct cable_pairing {
+ const char *name;
+ uint16_t source;
+ uint16_t vid;
+ uint16_t pid;
+ uint16_t version;
+ CablePairingType type;
+};
+
+static inline const struct cable_pairing *
+get_pairing(uint16_t vid, uint16_t pid)
+{
+ static const struct cable_pairing devices[] = {
+ {
+ .name = "Sony PLAYSTATION(R)3 Controller",
+ .source = 0x0002,
+ .vid = 0x054c,
+ .pid = 0x0268,
+ .version = 0x0000,
+ .type = CABLE_PAIRING_SIXAXIS,
+ },
+ {
+ .name = "Navigation Controller",
+ .source = 0x0002,
+ .vid = 0x054c,
+ .pid = 0x042f,
+ .version = 0x0000,
+ .type = CABLE_PAIRING_SIXAXIS,
+ },
+ {
+ .name = "Wireless Controller",
+ .source = 0x0002,
+ .vid = 0x054c,
+ .pid = 0x05c4,
+ .version = 0x0001,
+ .type = CABLE_PAIRING_DS4,
+ },
+ {
+ .name = "Wireless Controller",
+ .source = 0x0002,
+ .vid = 0x054c,
+ .pid = 0x09cc,
+ .version = 0x0001,
+ .type = CABLE_PAIRING_DS4,
+ },
+ };
+ guint i;
+
+ for (i = 0; i < G_N_ELEMENTS(devices); i++) {
+ if (devices[i].vid != vid)
+ continue;
+ if (devices[i].pid != pid)
+ continue;
+
+ return &devices[i];
+ }
+
+ return NULL;
+}
+
+#endif /* _SIXAXIS_H_ */
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2015,2016 Felipe F. Tonello <eu@felipetonello.com>
+ * Copyright (C) 2016 ROLI Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ *
+ */
+
+#include <glib.h>
+
+/* Avoid linkage problem on unit-tests */
+#ifndef MIDI_TEST
+#include "src/backtrace.h"
+#define MIDI_ASSERT(_expr) btd_assert(_expr)
+#else
+#define MIDI_ASSERT(_expr) g_assert(_expr)
+#endif
+#include "libmidi.h"
+
+inline static void buffer_append_byte(struct midi_buffer *buffer,
+ const uint8_t byte)
+{
+ buffer->data[buffer->len++] = byte;
+}
+
+inline static void buffer_append_data(struct midi_buffer *buffer,
+ const uint8_t *data, size_t size)
+{
+ memcpy(buffer->data + buffer->len, data, size);
+ buffer->len += size;
+}
+
+inline static uint8_t buffer_reverse_get(struct midi_buffer *buffer, size_t i)
+{
+ MIDI_ASSERT(buffer->len > i);
+ return buffer->data[buffer->len - (i + 1)];
+}
+
+inline static void buffer_reverse_set(struct midi_buffer *buffer, size_t i,
+ const uint8_t byte)
+{
+ MIDI_ASSERT(buffer->len > i);
+ buffer->data[buffer->len - (i + 1)] = byte;
+}
+
+inline static size_t parser_get_available_size(struct midi_write_parser *parser)
+{
+ return parser->stream_size - parser->midi_stream.len;
+}
+
+inline static uint8_t sysex_get(const snd_seq_event_t *ev, size_t i)
+{
+ MIDI_ASSERT(ev->data.ext.len > i);
+ return ((uint8_t*)ev->data.ext.ptr)[i];
+}
+
+inline static void append_timestamp_high_maybe(struct midi_write_parser *parser)
+{
+ uint8_t timestamp_high = 0x80;
+
+ if (midi_write_has_data(parser))
+ return;
+
+ parser->rtime = g_get_monotonic_time() / 1000; /* convert µs to ms */
+ timestamp_high |= (parser->rtime & 0x1F80) >> 7;
+ /* set timestampHigh */
+ buffer_append_byte(&parser->midi_stream, timestamp_high);
+}
+
+inline static void append_timestamp_low(struct midi_write_parser *parser)
+{
+ const uint8_t timestamp_low = 0x80 | (parser->rtime & 0x7F);
+ buffer_append_byte(&parser->midi_stream, timestamp_low);
+}
+
+int midi_write_init(struct midi_write_parser *parser, size_t buffer_size)
+{
+ int err;
+
+ parser->rtime = 0;
+ parser->rstatus = SND_SEQ_EVENT_NONE;
+ parser->stream_size = buffer_size;
+
+ parser->midi_stream.data = malloc(buffer_size);
+ if (!parser->midi_stream.data)
+ return -ENOMEM;
+
+ parser->midi_stream.len = 0;
+
+ err = snd_midi_event_new(buffer_size, &parser->midi_ev);
+ if (err < 0)
+ free(parser->midi_stream.data);
+
+ return err;
+}
+
+int midi_read_init(struct midi_read_parser *parser)
+{
+ int err;
+
+ parser->rstatus = 0;
+ parser->rtime = -1;
+ parser->timestamp = 0;
+ parser->timestamp_low = 0;
+ parser->timestamp_high = 0;
+
+ parser->sysex_stream.data = malloc(MIDI_SYSEX_MAX_SIZE);
+ if (!parser->sysex_stream.data)
+ return -ENOMEM;
+
+ parser->sysex_stream.len = 0;
+
+ err = snd_midi_event_new(MIDI_MSG_MAX_SIZE, &parser->midi_ev);
+ if (err < 0)
+ free(parser->sysex_stream.data);
+
+ return err;
+}
+
+/* Algorithm:
+ 1) check initial timestampLow:
+ if used_sysex == 0, then tsLow = 1, else tsLow = 0
+ 2) calculate sysex size of current packet:
+ 2a) first check special case:
+ if midi->out_length - 1 (tsHigh) - tsLow ==
+ sysex_length - used_sysex
+ size is: min(midi->out_length - 1 - tsLow,
+ sysex_length - used_sysex - 1)
+ 2b) else size is: min(midi->out_length - 1 - tsLow,
+ sysex_length - used_sysex)
+ 3) check if packet contains F7: fill respective tsLow byte
+*/
+static void read_ev_sysex(struct midi_write_parser *parser, const snd_seq_event_t *ev,
+ midi_read_ev_cb write_cb, void *user_data)
+{
+ unsigned int used_sysex = 0;
+
+ /* We need at least 2 bytes (timestampLow + F0) */
+ if (parser_get_available_size(parser) < 2) {
+ /* send current message and start new one */
+ write_cb(parser, user_data);
+ midi_write_reset(parser);
+ append_timestamp_high_maybe(parser);
+ }
+
+ /* timestampLow on initial F0 */
+ if (sysex_get(ev, 0) == 0xF0)
+ append_timestamp_low(parser);
+
+ do {
+ unsigned int size_of_sysex;
+
+ append_timestamp_high_maybe(parser);
+
+ size_of_sysex = MIN(parser_get_available_size(parser),
+ ev->data.ext.len - used_sysex);
+
+ if (parser_get_available_size(parser) == ev->data.ext.len - used_sysex)
+ size_of_sysex--;
+
+ buffer_append_data(&parser->midi_stream,
+ ev->data.ext.ptr + used_sysex,
+ size_of_sysex);
+ used_sysex += size_of_sysex;
+
+ if (parser_get_available_size(parser) <= 1 &&
+ buffer_reverse_get(&parser->midi_stream, 0) != 0xF7) {
+ write_cb(parser, user_data);
+ midi_write_reset(parser);
+ }
+ } while (used_sysex < ev->data.ext.len);
+
+ /* check for F7 and update respective timestampLow byte */
+ if (midi_write_has_data(parser) &&
+ buffer_reverse_get(&parser->midi_stream, 0) == 0xF7) {
+ /* remove 0xF7 from buffer, append timestamp and add 0xF7 back again */
+ parser->midi_stream.len--;
+ append_timestamp_low(parser);
+ buffer_append_byte(&parser->midi_stream, 0xF7);
+ }
+}
+
+static void read_ev_others(struct midi_write_parser *parser, const snd_seq_event_t *ev,
+ midi_read_ev_cb write_cb, void *user_data)
+{
+ int length;
+
+ /* check for running status */
+ if (parser->rstatus != ev->type) {
+ snd_midi_event_reset_decode(parser->midi_ev);
+ append_timestamp_low(parser);
+ }
+
+ /* each midi message has timestampLow byte to follow */
+ length = snd_midi_event_decode(parser->midi_ev,
+ parser->midi_stream.data +
+ parser->midi_stream.len,
+ parser_get_available_size(parser),
+ ev);
+
+ if (length == -ENOMEM) {
+ /* remove previously added timestampLow */
+ if (parser->rstatus != ev->type)
+ parser->midi_stream.len--;
+ write_cb(parser, user_data);
+ /* cleanup state for next packet */
+ snd_midi_event_reset_decode(parser->midi_ev);
+ midi_write_reset(parser);
+ append_timestamp_high_maybe(parser);
+ append_timestamp_low(parser);
+ length = snd_midi_event_decode(parser->midi_ev,
+ parser->midi_stream.data +
+ parser->midi_stream.len,
+ parser_get_available_size(parser),
+ ev);
+ }
+
+ if (length > 0)
+ parser->midi_stream.len += length;
+}
+
+void midi_read_ev(struct midi_write_parser *parser, const snd_seq_event_t *ev,
+ midi_read_ev_cb write_cb, void *user_data)
+{
+ MIDI_ASSERT(write_cb);
+
+ append_timestamp_high_maybe(parser);
+
+ /* SysEx is special case:
+ SysEx has two timestampLow bytes, before F0 and F7
+ */
+ if (ev->type == SND_SEQ_EVENT_SYSEX)
+ read_ev_sysex(parser, ev, write_cb, user_data);
+ else
+ read_ev_others(parser, ev, write_cb, user_data);
+
+ parser->rstatus = ev->type;
+
+ if (parser_get_available_size(parser) == 0) {
+ write_cb(parser, user_data);
+ midi_write_reset(parser);
+ }
+}
+
+static void update_ev_timestamp(struct midi_read_parser *parser,
+ snd_seq_event_t *ev, uint16_t ts_low)
+{
+ int delta_timestamp;
+ int delta_rtime;
+ int64_t rtime_current;
+ uint16_t timestamp;
+
+ /* time_low overwflow results on time_high to increment by one */
+ if (parser->timestamp_low > ts_low)
+ parser->timestamp_high++;
+
+ timestamp = (parser->timestamp_high << 7) | parser->timestamp_low;
+
+ rtime_current = g_get_monotonic_time() / 1000; /* convert µs to ms */
+ delta_timestamp = timestamp - (int)parser->timestamp;
+ delta_rtime = rtime_current - parser->rtime;
+
+ if (delta_rtime > MIDI_MAX_TIMESTAMP)
+ parser->rtime = rtime_current;
+ else {
+
+ /* If delta_timestamp is way to big than delta_rtime,
+ this means that the device sent a message in the past,
+ so we have to compensate for this. */
+ if (delta_timestamp > 7000 && delta_rtime < 1000)
+ delta_timestamp = 0;
+
+ /* check if timestamp did overflow */
+ if (delta_timestamp < 0) {
+ /* same timestamp in the past problem */
+ if ((delta_timestamp + MIDI_MAX_TIMESTAMP) > 7000 &&
+ delta_rtime < 1000)
+ delta_timestamp = 0;
+ else
+ delta_timestamp = delta_timestamp + MIDI_MAX_TIMESTAMP;
+ }
+
+ parser->rtime += delta_timestamp;
+ }
+
+ parser->timestamp += delta_timestamp;
+ if (parser->timestamp > MIDI_MAX_TIMESTAMP)
+ parser->timestamp %= MIDI_MAX_TIMESTAMP + 1;
+
+ /* set event timestamp */
+ /* TODO: update event timestamp here! */
+}
+
+static size_t handle_end_of_sysex(struct midi_read_parser *parser,
+ snd_seq_event_t *ev,
+ const uint8_t *data,
+ size_t sysex_length)
+{
+ uint8_t time_low;
+
+ /* At this time, timestampLow is copied as the last byte,
+ instead of 0xF7 */
+ buffer_append_data(&parser->sysex_stream, data, sysex_length);
+
+ time_low = buffer_reverse_get(&parser->sysex_stream, 0) & 0x7F;
+
+ /* Remove timestamp byte */
+ buffer_reverse_set(&parser->sysex_stream, 0, 0xF7);
+
+ /* Update event */
+ update_ev_timestamp(parser, ev, time_low);
+ snd_seq_ev_set_sysex(ev, parser->sysex_stream.len,
+ parser->sysex_stream.data);
+
+ return sysex_length + 1; /* +1 because of timestampLow */
+}
+
+
+
+size_t midi_read_raw(struct midi_read_parser *parser, const uint8_t *data,
+ size_t size, snd_seq_event_t *ev /* OUT */)
+{
+ size_t midi_size = 0;
+ size_t i = 0;
+ bool err = false;
+
+ if (parser->timestamp_high == 0)
+ parser->timestamp_high = data[i++] & 0x3F;
+
+ snd_midi_event_reset_encode(parser->midi_ev);
+
+ /* timestamp byte */
+ if (data[i] & 0x80) {
+ update_ev_timestamp(parser, ev, data[i] & 0x7F);
+
+ /* check for wrong BLE-MIDI message size */
+ if (++i == size) {
+ err = true;
+ goto _finish;
+ }
+ }
+
+ /* cleanup sysex_stream if message is broken or is a new SysEx */
+ if (data[i] >= 0x80 && data[i] != 0xF7 && parser->sysex_stream.len > 0)
+ parser->sysex_stream.len = 0;
+
+ switch (data[i]) {
+ case 0xF8 ... 0XFF:
+ /* System Real-Time Messages */
+ midi_size = 1;
+ break;
+
+ /* System Common Messages */
+ case 0xF0: /* SysEx Start */ {
+ uint8_t *pos;
+
+ /* cleanup Running Status Message */
+ parser->rstatus = 0;
+
+ /* Avoid parsing if SysEx is contained in one BLE packet */
+ if ((pos = memchr(data + i, 0xF7, size - i))) {
+ const size_t sysex_length = pos - (data + i);
+ midi_size = handle_end_of_sysex(parser, ev, data + i,
+ sysex_length);
+ } else {
+ buffer_append_data(&parser->sysex_stream, data + i, size - i);
+ err = true; /* Not an actual error, just incomplete message */
+ midi_size = size - i;
+ }
+
+ goto _finish;
+ }
+
+ case 0xF1:
+ case 0xF3:
+ midi_size = 2;
+ break;
+ case 0xF2:
+ midi_size = 3;
+ break;
+ case 0xF4:
+ case 0xF5: /* Ignore */
+ i++;
+ err = true;
+ goto _finish;
+ break;
+ case 0xF6:
+ midi_size = 1;
+ break;
+ case 0xF7: /* SysEx End */
+ buffer_append_byte(&parser->sysex_stream, 0xF7);
+ snd_seq_ev_set_sysex(ev, parser->sysex_stream.len,
+ parser->sysex_stream.data);
+
+ midi_size = 1; /* timestampLow was alredy processed */
+ goto _finish;
+
+ case 0x80 ... 0xEF:
+ /*
+ * Channel Voice Messages, Channel Mode Messages
+ * and Control Change Messages.
+ */
+ parser->rstatus = data[i];
+ midi_size = (data[i] >= 0xC0 && data[i] <= 0xDF) ? 2 : 3;
+ break;
+
+ case 0x00 ... 0x7F:
+
+ /* Check for SysEx messages */
+ if (parser->sysex_stream.len > 0) {
+ uint8_t *pos;
+
+ if ((pos = memchr(data + i, 0xF7, size - i))) {
+ const size_t sysex_length = pos - (data + i);
+ midi_size = handle_end_of_sysex(parser, ev, data + i,
+ sysex_length);
+ } else {
+ buffer_append_data(&parser->sysex_stream, data + i, size - i);
+ err = true; /* Not an actual error, just incomplete message */
+ midi_size = size - i;
+ }
+
+ goto _finish;
+ }
+
+ /* Running State Message was not set */
+ if (parser->rstatus == 0) {
+ midi_size = 1;
+ err = true;
+ goto _finish;
+ }
+
+ snd_midi_event_encode_byte(parser->midi_ev, parser->rstatus, ev);
+ midi_size = (parser->rstatus >= 0xC0 && parser->rstatus <= 0xDF) ? 1 : 2;
+ break;
+ }
+
+ if ((i + midi_size) > size) {
+ err = true;
+ goto _finish;
+ }
+
+ snd_midi_event_encode(parser->midi_ev, data + i, midi_size, ev);
+
+_finish:
+ if (err)
+ ev->type = SND_SEQ_EVENT_NONE;
+
+ return i + midi_size;
+}
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2015,2016 Felipe F. Tonello <eu@felipetonello.com>
+ * Copyright (C) 2016 ROLI Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ *
+ */
+
+#ifndef LIBMIDI_H
+#define LIBMIDI_H
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <alsa/asoundlib.h>
+
+#define MIDI_UUID "03B80E5A-EDE8-4B33-A751-6CE34EC4C700"
+#define MIDI_IO_UUID "7772E5DB-3868-4112-A1A9-F2669D106BF3"
+
+#define MIDI_MAX_TIMESTAMP 8191
+#define MIDI_MSG_MAX_SIZE 12
+#define MIDI_SYSEX_MAX_SIZE (4 * 1024)
+
+struct midi_buffer {
+ uint8_t *data;
+ size_t len;
+};
+
+/* MIDI I/O Write parser */
+
+struct midi_write_parser {
+ int64_t rtime; /* last writer's real time */
+ snd_seq_event_type_t rstatus; /* running status event type */
+ struct midi_buffer midi_stream; /* MIDI I/O byte stream */
+ size_t stream_size; /* what is the maximum size of the midi_stream array */
+ snd_midi_event_t *midi_ev; /* midi<->seq event */
+};
+
+int midi_write_init(struct midi_write_parser *parser, size_t buffer_size);
+
+static inline void midi_write_free(struct midi_write_parser *parser)
+{
+ free(parser->midi_stream.data);
+ snd_midi_event_free(parser->midi_ev);
+}
+
+static inline void midi_write_reset(struct midi_write_parser *parser)
+{
+ parser->rstatus = SND_SEQ_EVENT_NONE;
+ parser->midi_stream.len = 0;
+}
+
+static inline bool midi_write_has_data(const struct midi_write_parser *parser)
+{
+ return parser->midi_stream.len > 0;
+}
+
+static inline const uint8_t * midi_write_data(const struct midi_write_parser *parser)
+{
+ return parser->midi_stream.data;
+}
+
+static inline size_t midi_write_data_size(const struct midi_write_parser *parser)
+{
+ return parser->midi_stream.len;
+}
+
+typedef void (*midi_read_ev_cb)(const struct midi_write_parser *parser, void *);
+
+/* It creates BLE-MIDI raw packets from the a sequencer event. If the packet
+ is full, then it calls write_cb and resets its internal state as many times
+ as necessary.
+ */
+void midi_read_ev(struct midi_write_parser *parser, const snd_seq_event_t *ev,
+ midi_read_ev_cb write_cb, void *user_data);
+
+/* MIDI I/O Read parser */
+
+struct midi_read_parser {
+ uint8_t rstatus; /* running status byte */
+ int64_t rtime; /* last reader's real time */
+ int16_t timestamp; /* last MIDI-BLE timestamp */
+ int8_t timestamp_low; /* MIDI-BLE timestampLow from the current packet */
+ int8_t timestamp_high; /* MIDI-BLE timestampHigh from the current packet */
+ struct midi_buffer sysex_stream; /* SysEx stream */
+ snd_midi_event_t *midi_ev; /* midi<->seq event */
+};
+
+int midi_read_init(struct midi_read_parser *parser);
+
+static inline void midi_read_free(struct midi_read_parser *parser)
+{
+ free(parser->sysex_stream.data);
+ snd_midi_event_free(parser->midi_ev);
+}
+
+static inline void midi_read_reset(struct midi_read_parser *parser)
+{
+ parser->rstatus = 0;
+ parser->timestamp_low = 0;
+ parser->timestamp_high = 0;
+}
+
+/* Parses raw BLE-MIDI messages and populates a sequencer event representing the
+ current MIDI message. It returns how much raw data was processed.
+ */
+size_t midi_read_raw(struct midi_read_parser *parser, const uint8_t *data,
+ size_t size, snd_seq_event_t *ev /* OUT */);
+
+#endif /* LIBMIDI_H */
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2015,2016 Felipe F. Tonello <eu@felipetonello.com>
+ * Copyright (C) 2016 ROLI Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Information about this plugin:
+ *
+ * This plugin implements the MIDI over Bluetooth Low-Energy (BLE-MIDI) 1.0
+ * specification as published by MMA in November/2015.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <alsa/asoundlib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/uuid.h"
+
+#include "src/plugin.h"
+#include "src/adapter.h"
+#include "src/device.h"
+#include "src/profile.h"
+#include "src/service.h"
+#include "src/shared/util.h"
+#include "src/shared/att.h"
+#include "src/shared/queue.h"
+#include "src/shared/gatt-db.h"
+#include "src/shared/gatt-client.h"
+#include "src/shared/io.h"
+#include "src/log.h"
+#include "attrib/att.h"
+
+#include "libmidi.h"
+
+struct midi {
+ struct btd_device *dev;
+ struct gatt_db *db;
+ struct bt_gatt_client *client;
+ unsigned int io_cb_id;
+ struct io *io;
+ uint16_t midi_io_handle;
+
+ /* ALSA handlers */
+ snd_seq_t *seq_handle;
+ int seq_client_id;
+ int seq_port_id;
+
+ /* MIDI parser*/
+ struct midi_read_parser midi_in;
+ struct midi_write_parser midi_out;
+};
+
+static bool midi_write_cb(struct io *io, void *user_data)
+{
+ struct midi *midi = user_data;
+ int err;
+
+ void foreach_cb(const struct midi_write_parser *parser, void *user_data) {
+ struct midi *midi = user_data;
+ bt_gatt_client_write_without_response(midi->client,
+ midi->midi_io_handle,
+ false,
+ midi_write_data(parser),
+ midi_write_data_size(parser));
+ };
+
+ do {
+ snd_seq_event_t *event = NULL;
+
+ err = snd_seq_event_input(midi->seq_handle, &event);
+
+ if (err < 0 || !event)
+ break;
+
+ midi_read_ev(&midi->midi_out, event, foreach_cb, midi);
+
+ } while (err > 0);
+
+ if (midi_write_has_data(&midi->midi_out))
+ bt_gatt_client_write_without_response(midi->client,
+ midi->midi_io_handle,
+ false,
+ midi_write_data(&midi->midi_out),
+ midi_write_data_size(&midi->midi_out));
+
+ midi_write_reset(&midi->midi_out);
+
+ return true;
+}
+
+static void midi_io_value_cb(uint16_t value_handle, const uint8_t *value,
+ uint16_t length, void *user_data)
+{
+ struct midi *midi = user_data;
+ snd_seq_event_t ev;
+ unsigned int i = 0;
+
+ if (length < 3) {
+ warn("MIDI I/O: Wrong packet format: length is %u bytes but it should "
+ "be at least 3 bytes", length);
+ return;
+ }
+
+ snd_seq_ev_clear(&ev);
+ snd_seq_ev_set_source(&ev, midi->seq_port_id);
+ snd_seq_ev_set_subs(&ev);
+ snd_seq_ev_set_direct(&ev);
+
+ midi_read_reset(&midi->midi_in);
+
+ while (i < length) {
+ size_t count = midi_read_raw(&midi->midi_in, value + i, length - i, &ev);
+
+ if (count == 0)
+ goto _err;
+
+ if (ev.type != SND_SEQ_EVENT_NONE)
+ snd_seq_event_output_direct(midi->seq_handle, &ev);
+
+ i += count;
+ }
+
+ return;
+
+_err:
+ error("Wrong BLE-MIDI message");
+}
+
+static void midi_io_ccc_written_cb(uint16_t att_ecode, void *user_data)
+{
+ if (att_ecode != 0) {
+ error("MIDI I/O: notifications not enabled %s",
+ att_ecode2str(att_ecode));
+ return;
+ }
+
+ DBG("MIDI I/O: notification enabled");
+}
+
+static void midi_io_initial_read_cb(bool success, uint8_t att_ecode,
+ const uint8_t *value, uint16_t length,
+ void *user_data)
+{
+ struct midi *midi = user_data;
+
+ if (!success) {
+ error("MIDI I/O: Failed to read initial request");
+ return;
+ }
+
+ /* request notify */
+ midi->io_cb_id =
+ bt_gatt_client_register_notify(midi->client,
+ midi->midi_io_handle,
+ midi_io_ccc_written_cb,
+ midi_io_value_cb,
+ midi,
+ NULL);
+}
+
+static void handle_midi_io(struct midi *midi, uint16_t value_handle)
+{
+ DBG("MIDI I/O handle: 0x%04x", value_handle);
+
+ midi->midi_io_handle = value_handle;
+
+ /*
+ * The BLE-MIDI 1.0 spec specifies that The Central shall attempt to
+ * read the MIDI I/O characteristic of the Peripheral right after
+ * estrablhishing a connection with the accessory.
+ */
+ if (!bt_gatt_client_read_value(midi->client,
+ value_handle,
+ midi_io_initial_read_cb,
+ midi,
+ NULL))
+ DBG("MIDI I/O: Failed to send request to read initial value");
+}
+
+static void handle_characteristic(struct gatt_db_attribute *attr,
+ void *user_data)
+{
+ struct midi *midi = user_data;
+ uint16_t value_handle;
+ bt_uuid_t uuid, midi_io_uuid;
+
+ if (!gatt_db_attribute_get_char_data(attr, NULL, &value_handle, NULL,
+ NULL, &uuid)) {
+ error("Failed to obtain characteristic data");
+ return;
+ }
+
+ bt_string_to_uuid(&midi_io_uuid, MIDI_IO_UUID);
+
+ if (bt_uuid_cmp(&midi_io_uuid, &uuid) == 0)
+ handle_midi_io(midi, value_handle);
+ else {
+ char uuid_str[MAX_LEN_UUID_STR];
+
+ bt_uuid_to_string(&uuid, uuid_str, sizeof(uuid_str));
+ DBG("Unsupported characteristic: %s", uuid_str);
+ }
+}
+
+static void foreach_midi_service(struct gatt_db_attribute *attr,
+ void *user_data)
+{
+ struct midi *midi = user_data;
+
+ gatt_db_service_foreach_char(attr, handle_characteristic, midi);
+}
+
+static int midi_device_probe(struct btd_service *service)
+{
+ struct btd_device *device = btd_service_get_device(service);
+ struct midi *midi;
+ char addr[18];
+
+ ba2str(device_get_address(device), addr);
+ DBG("MIDI GATT Driver profile probe (%s)", addr);
+
+ /* Ignore, if we were probed for this device already */
+ midi = btd_service_get_user_data(service);
+ if (midi) {
+ error("Profile probed twice for the same device!");
+ return -EADDRINUSE;
+ }
+
+ midi = g_new0(struct midi, 1);
+ if (!midi)
+ return -ENOMEM;
+
+ midi->dev = btd_device_ref(device);
+
+ btd_service_set_user_data(service, midi);
+
+ return 0;
+}
+
+static void midi_device_remove(struct btd_service *service)
+{
+ struct btd_device *device = btd_service_get_device(service);
+ struct midi *midi;
+ char addr[18];
+
+ ba2str(device_get_address(device), addr);
+ DBG("MIDI GATT Driver profile remove (%s)", addr);
+
+ midi = btd_service_get_user_data(service);
+ if (!midi) {
+ error("MIDI Service not handled by profile");
+ return;
+ }
+
+ btd_device_unref(midi->dev);
+ g_free(midi);
+}
+
+static int midi_accept(struct btd_service *service)
+{
+ struct btd_device *device = btd_service_get_device(service);
+ struct gatt_db *db = btd_device_get_gatt_db(device);
+ struct bt_gatt_client *client = btd_device_get_gatt_client(device);
+ bt_uuid_t midi_uuid;
+ struct pollfd pfd;
+ struct midi *midi;
+ char addr[18];
+ char device_name[MAX_NAME_LENGTH + 11]; /* 11 = " Bluetooth\0"*/
+ int err;
+ snd_seq_client_info_t *info;
+
+ ba2str(device_get_address(device), addr);
+ DBG("MIDI GATT Driver profile accept (%s)", addr);
+
+ midi = btd_service_get_user_data(service);
+ if (!midi) {
+ error("MIDI Service not handled by profile");
+ return -ENODEV;
+ }
+
+ /* Port Name */
+ memset(device_name, 0, sizeof(device_name));
+ if (device_name_known(device))
+ device_get_name(device, device_name, sizeof(device_name));
+ else
+ strncpy(device_name, addr, sizeof(addr));
+
+ /* ALSA Sequencer Client and Port Setup */
+ err = snd_seq_open(&midi->seq_handle, "default", SND_SEQ_OPEN_DUPLEX, 0);
+ if (err < 0) {
+ error("Could not open ALSA Sequencer: %s (%d)", snd_strerror(err), err);
+ return err;
+ }
+
+ err = snd_seq_nonblock(midi->seq_handle, SND_SEQ_NONBLOCK);
+ if (err < 0) {
+ error("Could not set nonblock mode: %s (%d)", snd_strerror(err), err);
+ goto _err_handle;
+ }
+
+ err = snd_seq_set_client_name(midi->seq_handle, device_name);
+ if (err < 0) {
+ error("Could not configure ALSA client: %s (%d)", snd_strerror(err), err);
+ goto _err_handle;
+ }
+
+ err = snd_seq_client_id(midi->seq_handle);
+ if (err < 0) {
+ error("Could retreive ALSA client: %s (%d)", snd_strerror(err), err);
+ goto _err_handle;
+ }
+ midi->seq_client_id = err;
+
+ err = snd_seq_create_simple_port(midi->seq_handle, strcat(device_name, " Bluetooth"),
+ SND_SEQ_PORT_CAP_READ |
+ SND_SEQ_PORT_CAP_WRITE |
+ SND_SEQ_PORT_CAP_SUBS_READ |
+ SND_SEQ_PORT_CAP_SUBS_WRITE,
+ SND_SEQ_PORT_TYPE_MIDI_GENERIC |
+ SND_SEQ_PORT_TYPE_HARDWARE);
+ if (err < 0) {
+ error("Could not create ALSA port: %s (%d)", snd_strerror(err), err);
+ goto _err_handle;
+ }
+ midi->seq_port_id = err;
+
+ snd_seq_client_info_alloca(&info);
+ err = snd_seq_get_client_info(midi->seq_handle, info);
+ if (err < 0)
+ goto _err_port;
+
+ /* list of relevant sequencer events */
+ snd_seq_client_info_event_filter_add(info, SND_SEQ_EVENT_NOTEOFF);
+ snd_seq_client_info_event_filter_add(info, SND_SEQ_EVENT_NOTEON);
+ snd_seq_client_info_event_filter_add(info, SND_SEQ_EVENT_KEYPRESS);
+ snd_seq_client_info_event_filter_add(info, SND_SEQ_EVENT_CONTROLLER);
+ snd_seq_client_info_event_filter_add(info, SND_SEQ_EVENT_PGMCHANGE);
+ snd_seq_client_info_event_filter_add(info, SND_SEQ_EVENT_CHANPRESS);
+ snd_seq_client_info_event_filter_add(info, SND_SEQ_EVENT_PITCHBEND);
+ snd_seq_client_info_event_filter_add(info, SND_SEQ_EVENT_SYSEX);
+ snd_seq_client_info_event_filter_add(info, SND_SEQ_EVENT_QFRAME);
+ snd_seq_client_info_event_filter_add(info, SND_SEQ_EVENT_SONGPOS);
+ snd_seq_client_info_event_filter_add(info, SND_SEQ_EVENT_SONGSEL);
+ snd_seq_client_info_event_filter_add(info, SND_SEQ_EVENT_TUNE_REQUEST);
+ snd_seq_client_info_event_filter_add(info, SND_SEQ_EVENT_CLOCK);
+ snd_seq_client_info_event_filter_add(info, SND_SEQ_EVENT_START);
+ snd_seq_client_info_event_filter_add(info, SND_SEQ_EVENT_CONTINUE);
+ snd_seq_client_info_event_filter_add(info, SND_SEQ_EVENT_STOP);
+ snd_seq_client_info_event_filter_add(info, SND_SEQ_EVENT_SENSING);
+ snd_seq_client_info_event_filter_add(info, SND_SEQ_EVENT_RESET);
+ snd_seq_client_info_event_filter_add(info, SND_SEQ_EVENT_CONTROL14);
+ snd_seq_client_info_event_filter_add(info, SND_SEQ_EVENT_NONREGPARAM);
+ snd_seq_client_info_event_filter_add(info, SND_SEQ_EVENT_REGPARAM);
+
+ err = snd_seq_set_client_info(midi->seq_handle, info);
+ if (err < 0)
+ goto _err_port;
+
+
+ /* Input file descriptors */
+ snd_seq_poll_descriptors(midi->seq_handle, &pfd, 1, POLLIN);
+
+ midi->io = io_new(pfd.fd);
+ if (!midi->io) {
+ error("Could not allocate I/O eventloop");
+ goto _err_port;
+ }
+
+ io_set_read_handler(midi->io, midi_write_cb, midi, NULL);
+
+ midi->db = gatt_db_ref(db);
+ midi->client = bt_gatt_client_ref(client);
+
+ err = midi_read_init(&midi->midi_in);
+ if (err < 0) {
+ error("Could not initialise MIDI input parser");
+ goto _err_port;
+ }
+
+ err = midi_write_init(&midi->midi_out, bt_gatt_client_get_mtu(midi->client) - 3);
+ if (err < 0) {
+ error("Could not initialise MIDI output parser");
+ goto _err_midi;
+ }
+
+ bt_string_to_uuid(&midi_uuid, MIDI_UUID);
+ gatt_db_foreach_service(db, &midi_uuid, foreach_midi_service, midi);
+
+ btd_service_connecting_complete(service, 0);
+
+ return 0;
+
+_err_midi:
+ midi_read_free(&midi->midi_in);
+
+_err_port:
+ snd_seq_delete_simple_port(midi->seq_handle, midi->seq_port_id);
+
+_err_handle:
+ snd_seq_close(midi->seq_handle);
+ midi->seq_handle = NULL;
+
+ btd_service_connecting_complete(service, err);
+
+ return err;
+}
+
+static int midi_disconnect(struct btd_service *service)
+{
+ struct btd_device *device = btd_service_get_device(service);
+ struct midi *midi;
+ char addr[18];
+
+ ba2str(device_get_address(device), addr);
+ DBG("MIDI GATT Driver profile disconnect (%s)", addr);
+
+ midi = btd_service_get_user_data(service);
+ if (!midi) {
+ error("MIDI Service not handled by profile");
+ return -ENODEV;
+ }
+
+ midi_read_free(&midi->midi_in);
+ midi_write_free(&midi->midi_out);
+ io_destroy(midi->io);
+ snd_seq_delete_simple_port(midi->seq_handle, midi->seq_port_id);
+ midi->seq_port_id = 0;
+ snd_seq_close(midi->seq_handle);
+ midi->seq_handle = NULL;
+
+ /* Clean-up any old client/db */
+ bt_gatt_client_unregister_notify(midi->client, midi->io_cb_id);
+ bt_gatt_client_unref(midi->client);
+ gatt_db_unref(midi->db);
+
+ btd_service_disconnecting_complete(service, 0);
+
+ return 0;
+}
+
+static struct btd_profile midi_profile = {
+ .name = "MIDI GATT Driver",
+ .remote_uuid = MIDI_UUID,
+ .priority = BTD_PROFILE_PRIORITY_HIGH,
+ .auto_connect = true,
+
+ .device_probe = midi_device_probe,
+ .device_remove = midi_device_remove,
+
+ .accept = midi_accept,
+
+ .disconnect = midi_disconnect,
+};
+
+static int midi_init(void)
+{
+ return btd_profile_register(&midi_profile);
+}
+
+static void midi_exit(void)
+{
+ btd_profile_unregister(&midi_profile);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(midi, VERSION, BLUETOOTH_PLUGIN_PRIORITY_HIGH,
+ midi_init, midi_exit);
return BNEP_CONN_INVALID_DST;
if (memcmp(&source[4], bt_base, sizeof(bt_base)) != 0)
return BNEP_CONN_INVALID_SRC;
-
- /* Intentional no-break */
-
+ /* fall through */
case 4: /* UUID32 */
val = get_be32(dest);
if (val > 0xffff)
case 16:
if (memcmp(&req->service[4], bt_base, sizeof(bt_base)) != 0)
break;
-
- /* Intentional no-brake */
-
+ /* fall through */
case 4:
val = get_be32(req->service);
if (val > 0xffff)
+++ /dev/null
-/*
- * BlueZ - Bluetooth protocol stack for Linux
- *
- * SAP Driver for ST-Ericsson U8500 platform
- *
- * Copyright (C) 2010-2011 ST-Ericsson SA
- *
- * Author: Waldemar Rymarkiewicz <waldemar.rymarkiewicz@tieto.com> for
- * ST-Ericsson.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <errno.h>
-#include <glib.h>
-#include <string.h>
-
-#include <sys/socket.h>
-#include <sys/un.h>
-
-#include "src/log.h"
-#include "sap.h"
-
-#define STE_SIMD_SOCK "/dev/socket/catd_a"
-#define STE_CLIENT_TAG 0x0000
-
-#define sap_error(fmt, arg...) do { \
- error("STE U8500 SAP: " fmt, ## arg); \
- } while (0)
-
-#define sap_info(fmt, arg...) do { \
- info("STE U8500 SAP: " fmt, ## arg); \
- } while (0)
-
-struct ste_message {
- uint16_t len;
- uint16_t id;
- uint32_t client_tag;
- uint8_t payload[0];
-} __attribute__((packed));
-
-#define STE_MSG_PAYLOAD_SIZE(msg) (msg->len - sizeof(*msg) + sizeof(msg->len))
-
-enum ste_protocol {
- STE_START_SAP_REQ = 0x2D01,
- STE_START_SAP_RSP = 0x2E01,
- STE_END_SAP_REQ = 0x2D02,
- STE_END_SAP_RSP = 0x2E02,
- STE_POWER_OFF_REQ = 0x2D03,
- STE_POWER_OFF_RSP = 0x2E03,
- STE_POWER_ON_REQ = 0x2D04,
- STE_POWER_ON_RSP = 0x2E04,
- STE_RESET_REQ = 0x2D05,
- STE_RESET_RSP = 0x2E05,
- STE_SEND_APDU_REQ = 0x2D06,
- STE_SEND_APDU_RSP = 0x2E06,
- STE_GET_ATR_REQ = 0x2D07,
- STE_GET_ATR_RSP = 0x2E07,
- STE_GET_STATUS_REQ = 0x2D08,
- STE_GET_STATUS_RSP = 0x2E08,
- STE_STATUS_IND = 0x2F02,
- STE_SIM_READY_IND = 0x2F03,
-};
-
-enum ste_msg {
- STE_SEND_APDU_MSG,
- STE_GET_ATR_MSG,
- STE_POWER_OFF_MSG,
- STE_POWER_ON_MSG,
- STE_RESET_MSG,
- STE_GET_STATUS_MSG,
- STE_MSG_MAX,
-};
-
-enum ste_status {
- STE_STATUS_OK = 0x00000000,
- STE_STATUS_FAILURE = 0x00000001,
- STE_STATUS_BUSY_CALL = 0x00000002,
-};
-
-enum ste_card_status {
- STE_CARD_STATUS_UNKNOWN = 0x00,
- STE_CARD_STATUS_ACTIVE = 0x01,
- STE_CARD_STATUS_NOT_ACTIVE = 0x02,
- STE_CARD_STATUS_MISSING = 0x03,
- STE_CARD_STATUS_INVALID = 0x04,
- STE_CARD_STATUS_DISCONNECTED = 0x05,
-};
-
-/* Card reader status bits as described in GSM 11.14, Section 12.33
- * Bits 0-2 are for card reader identity and always zeros. */
-#define ICC_READER_REMOVABLE (1 << 3)
-#define ICC_READER_PRESENT (1 << 4)
-#define ICC_READER_ID1 (1 << 5)
-#define ICC_READER_CARD_PRESENT (1 << 6)
-#define ICC_READER_CARD_POWERED (1 << 7)
-
-enum ste_state {
- STE_DISABLED, /* Reader not present or removed */
- STE_POWERED_OFF, /* Card in the reader but powered off */
- STE_NO_CARD, /* No card in the reader */
- STE_ENABLED, /* Card in the reader and powered on */
- STE_SIM_BUSY, /* Modem is busy with ongoing call.*/
- STE_STATE_MAX
-};
-
-struct ste_u8500 {
- GIOChannel *io;
- enum ste_state state;
- void *sap_data;
-};
-
-typedef int(*recv_state_change_cb)(void *sap, uint8_t result);
-typedef int(*recv_pdu_cb)(void *sap, uint8_t result, uint8_t *data,
- uint16_t len);
-
-static struct ste_u8500 u8500;
-
-static const uint8_t sim2sap_result[STE_MSG_MAX][STE_STATE_MAX] = {
- /* SAP results for SEND APDU message */
- {
- SAP_RESULT_ERROR_NOT_ACCESSIBLE, /* STE_DISABLED */
- SAP_RESULT_ERROR_POWERED_OFF, /* STE_POWERED_OFF */
- SAP_RESULT_ERROR_CARD_REMOVED, /* STE_NO_CARD */
- SAP_RESULT_ERROR_NO_REASON /* STE_ENABLED */
- },
-
- /* SAP results for GET_ATR message */
- {
- SAP_RESULT_ERROR_NO_REASON,
- SAP_RESULT_ERROR_POWERED_OFF,
- SAP_RESULT_ERROR_CARD_REMOVED,
- SAP_RESULT_ERROR_NO_REASON
- },
-
- /* SAP results POWER OFF message */
- {
- SAP_RESULT_ERROR_NO_REASON,
- SAP_RESULT_ERROR_POWERED_OFF,
- SAP_RESULT_ERROR_CARD_REMOVED,
- SAP_RESULT_ERROR_NO_REASON
- },
-
- /* SAP results POWER ON message */
- {
- SAP_RESULT_ERROR_NO_REASON,
- SAP_RESULT_ERROR_NOT_ACCESSIBLE,
- SAP_RESULT_ERROR_CARD_REMOVED,
- SAP_RESULT_ERROR_POWERED_ON
- },
-
- /* SAP results SIM RESET message */
- {
- SAP_RESULT_ERROR_NO_REASON,
- SAP_RESULT_ERROR_POWERED_OFF,
- SAP_RESULT_ERROR_CARD_REMOVED,
- SAP_RESULT_ERROR_NOT_ACCESSIBLE
- },
-
- /* SAP results GET STATUS message */
- {
- SAP_RESULT_ERROR_NO_REASON,
- SAP_RESULT_ERROR_NO_REASON,
- SAP_RESULT_ERROR_NO_REASON,
- SAP_RESULT_ERROR_NO_REASON
- }
-};
-
-static uint8_t get_sap_result(enum ste_msg msg, uint32_t status)
-{
- if (!u8500.io)
- return SAP_RESULT_ERROR_NO_REASON;
-
- switch (status) {
- case STE_STATUS_OK:
- return SAP_RESULT_OK;
-
- case STE_STATUS_FAILURE:
- return sim2sap_result[msg][u8500.state];
-
- default:
- DBG("Can't convert a result (status %u)", status);
- return SAP_RESULT_ERROR_NO_REASON;
- }
-}
-
-static int get_sap_reader_status(uint32_t card_status, uint8_t *icc_status)
-{
- /* Card reader is present, not removable and not ID-1 size. */
- *icc_status = ICC_READER_PRESENT;
-
- switch (card_status) {
- case STE_CARD_STATUS_ACTIVE:
- *icc_status |= ICC_READER_CARD_POWERED;
-
- case STE_CARD_STATUS_NOT_ACTIVE:
- case STE_CARD_STATUS_INVALID:
- *icc_status |= ICC_READER_CARD_PRESENT;
-
- case STE_CARD_STATUS_MISSING:
- case STE_CARD_STATUS_DISCONNECTED:
- return 0;
-
- default:
- DBG("Can't convert reader status %u", card_status);
-
- case STE_CARD_STATUS_UNKNOWN:
- return -1;
- }
-}
-
-static uint8_t get_sap_status_change(uint32_t card_status)
-{
- if (!u8500.io)
- return SAP_STATUS_CHANGE_UNKNOWN_ERROR;
-
- switch (card_status) {
- case STE_CARD_STATUS_UNKNOWN:
- return SAP_STATUS_CHANGE_UNKNOWN_ERROR;
-
- case STE_CARD_STATUS_ACTIVE:
- u8500.state = STE_ENABLED;
- return SAP_STATUS_CHANGE_CARD_RESET;
-
- case STE_CARD_STATUS_NOT_ACTIVE:
- u8500.state = STE_DISABLED;
- return SAP_STATUS_CHANGE_CARD_NOT_ACCESSIBLE;
-
- case STE_CARD_STATUS_MISSING:
- u8500.state = STE_DISABLED;
- return SAP_STATUS_CHANGE_CARD_REMOVED;
-
- case STE_CARD_STATUS_INVALID:
- u8500.state = STE_DISABLED;
- return SAP_STATUS_CHANGE_CARD_NOT_ACCESSIBLE;
-
- default:
- DBG("Can't convert status change %u", card_status);
- return SAP_STATUS_CHANGE_UNKNOWN_ERROR;
- }
-}
-
-static int send_message(GIOChannel *io, void *buf, size_t size)
-{
- gsize written;
-
- SAP_VDBG("io %p, size %zu", io, size);
-
- if (g_io_channel_write_chars(io, buf, size, &written, NULL) !=
- G_IO_STATUS_NORMAL)
- return -EIO;
-
- return written;
-}
-
-static int send_request(GIOChannel *io, uint16_t id,
- struct sap_parameter *param)
-{
- int ret;
- struct ste_message *msg;
- size_t size = sizeof(*msg);
-
- SAP_VDBG("io %p", io);
-
- if (param)
- size += param->len;
-
- msg = g_try_malloc0(size);
- if (!msg) {
- sap_error("sending request failed: %s", strerror(ENOMEM));
- return -ENOMEM;
- }
-
- msg->len = size - sizeof(msg->len);
- msg->id = id;
- msg->client_tag = STE_CLIENT_TAG;
-
- if (param)
- memcpy(msg->payload, param->val, param->len);
-
- ret = send_message(io, msg, size);
- if (ret < 0) {
- sap_error("sending request failed: %s", strerror(-ret));
- } else if (ret != (int) size) {
- sap_error("sending request failed: %d out of %zu bytes sent",
- ret, size);
- ret = -EIO;
- }
-
- g_free(msg);
-
- return ret;
-}
-
-static void recv_status(uint32_t status)
-{
- sap_status_ind(u8500.sap_data, get_sap_status_change(status));
-}
-
-static void recv_card_status(uint32_t status, uint8_t *param)
-{
- uint32_t card_status;
- uint8_t result;
- uint8_t iccrs;
-
- if (status != STE_STATUS_OK)
- return;
-
- memcpy(&card_status, param, sizeof(card_status));
-
- if (get_sap_reader_status(card_status, &iccrs) < 0)
- result = SAP_RESULT_ERROR_NO_REASON;
- else
- result = get_sap_result(STE_GET_STATUS_MSG, status);
-
- sap_transfer_card_reader_status_rsp(u8500.sap_data, result, iccrs);
-}
-
-static void recv_state_change(uint32_t ste_msg, uint32_t status,
- uint32_t new_state, recv_state_change_cb callback)
-{
- if (status != STE_STATUS_OK)
- return;
-
- u8500.state = new_state;
-
- if (callback)
- callback(u8500.sap_data, get_sap_result(ste_msg, status));
-}
-
-static void recv_pdu(uint32_t ste_msg, struct ste_message *msg, uint32_t status,
- uint8_t *param, recv_pdu_cb callback)
-{
- uint8_t *data = NULL;
- uint8_t result;
- int size = 0;
-
- if (status == STE_STATUS_OK) {
- data = param;
- size = STE_MSG_PAYLOAD_SIZE(msg) - sizeof(status);
- }
-
- result = get_sap_result(ste_msg, status);
-
- if (callback)
- callback(u8500.sap_data, result, data, size);
-}
-
-static void simd_close(void)
-{
- DBG("io %p", u8500.io);
-
- if (u8500.io) {
- g_io_channel_shutdown(u8500.io, TRUE, NULL);
- g_io_channel_unref(u8500.io);
- }
-
- u8500.state = STE_DISABLED;
- u8500.io = NULL;
- u8500.sap_data = NULL;
-}
-
-static void recv_sim_ready(void)
-{
- sap_info("sim is ready. Try to connect again");
-
- if (send_request(u8500.io, STE_START_SAP_REQ, NULL) < 0) {
- sap_connect_rsp(u8500.sap_data, SAP_STATUS_CONNECTION_FAILED);
- simd_close();
- }
-}
-
-static void recv_connect_rsp(uint32_t status)
-{
- switch (status) {
- case STE_STATUS_OK:
- if (u8500.state != STE_SIM_BUSY)
- sap_connect_rsp(u8500.sap_data, SAP_STATUS_OK);
- break;
- case STE_STATUS_BUSY_CALL:
- if (u8500.state != STE_SIM_BUSY) {
- sap_connect_rsp(u8500.sap_data,
- SAP_STATUS_OK_ONGOING_CALL);
-
- u8500.state = STE_SIM_BUSY;
- }
- break;
- default:
- sap_connect_rsp(u8500.sap_data, SAP_STATUS_CONNECTION_FAILED);
- simd_close();
- break;
- }
-}
-
-static void recv_response(struct ste_message *msg)
-{
- uint32_t status;
- uint8_t *param;
-
- SAP_VDBG("msg_id 0x%x", msg->id);
-
- if (msg->id == STE_END_SAP_RSP) {
- sap_disconnect_rsp(u8500.sap_data);
- simd_close();
- return;
- }
-
- param = msg->payload;
- memcpy(&status, param, sizeof(status));
- param += sizeof(status);
-
- SAP_VDBG("status 0x%x", status);
-
- switch (msg->id) {
- case STE_START_SAP_RSP:
- recv_connect_rsp(status);
- break;
- case STE_SEND_APDU_RSP:
- recv_pdu(STE_SEND_APDU_MSG, msg, status, param,
- sap_transfer_apdu_rsp);
- break;
-
- case STE_GET_ATR_RSP:
- recv_pdu(STE_GET_ATR_MSG, msg, status, param,
- sap_transfer_atr_rsp);
- break;
-
- case STE_POWER_OFF_RSP:
- recv_state_change(STE_POWER_OFF_MSG, status, STE_POWERED_OFF,
- sap_power_sim_off_rsp);
- break;
-
- case STE_POWER_ON_RSP:
- recv_state_change(STE_POWER_ON_MSG, status, STE_ENABLED,
- sap_power_sim_on_rsp);
- break;
-
- case STE_RESET_RSP:
- recv_state_change(STE_RESET_MSG, status, STE_ENABLED,
- sap_reset_sim_rsp);
- break;
-
- case STE_GET_STATUS_RSP:
- recv_card_status(status, param);
- break;
-
- case STE_STATUS_IND:
- recv_status(status);
- break;
-
- case STE_SIM_READY_IND:
- recv_sim_ready();
- break;
-
- default:
- sap_error("unsupported message received (id 0x%x)", msg->id);
- }
-}
-
-static int recv_message(void *buf, size_t size)
-{
- uint8_t *iter = buf;
- struct ste_message *msg = buf;
-
- do {
- SAP_VDBG("size %zu msg->len %u.", size, msg->len);
-
- if (size < sizeof(*msg)) {
- sap_error("invalid message received (%zu bytes)", size);
- return -EBADMSG;
- }
-
- /* Message must be complete. */
- if (size < (msg->len + sizeof(msg->len))) {
- sap_error("incomplete message received (%zu bytes)",
- size);
- return -EBADMSG;
- }
-
- recv_response(msg);
-
- /* Reduce total buffer size by just handled frame size. */
- size -= msg->len + sizeof(msg->len);
-
- /* Move msg pointer to the next message if any. */
- iter += msg->len + sizeof(msg->len);
- msg = (struct ste_message *)iter;
- } while (size > 0);
-
- return 0;
-}
-
-static gboolean simd_data_cb(GIOChannel *io, GIOCondition cond, gpointer data)
-{
- char buf[SAP_BUF_SIZE];
- gsize bytes_read;
- GIOStatus gstatus;
-
- if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR)) {
- DBG("Error condition on sim socket (0x%x)", cond);
- return FALSE;
- }
-
- gstatus = g_io_channel_read_chars(io, buf, sizeof(buf), &bytes_read,
- NULL);
- if (gstatus != G_IO_STATUS_NORMAL) {
- sap_error("error while reading from channel (%d)", gstatus);
- return TRUE;
- }
-
- if (recv_message(buf, bytes_read) < 0)
- sap_error("error while parsing STE Sim message");
-
- return TRUE;
-}
-
-static void simd_watch(int sock, void *sap_data)
-{
- GIOChannel *io;
-
- DBG("sock %d, sap_data %p ", sock, sap_data);
-
- io = g_io_channel_unix_new(sock);
-
- g_io_channel_set_close_on_unref(io, TRUE);
- g_io_channel_set_encoding(io, NULL, NULL);
- g_io_channel_set_buffered(io, FALSE);
-
- u8500.io = io;
- u8500.sap_data = sap_data;
- u8500.state = STE_DISABLED;
-
- g_io_add_watch_full(io, G_PRIORITY_DEFAULT,
- G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
- simd_data_cb, NULL, NULL);
-}
-
-static int simd_connect(void *sap_data)
-{
- struct sockaddr_un addr;
- int sock;
- int err;
-
- /* Already connected to simd */
- if (u8500.io)
- return -EALREADY;
-
- sock = socket(PF_UNIX, SOCK_STREAM, 0);
- if (sock < 0) {
- err = -errno;
- sap_error("creating socket failed: %s", strerror(-err));
- return err;
- }
-
- memset(&addr, 0, sizeof(addr));
- addr.sun_family = AF_UNIX;
- memcpy(addr.sun_path, STE_SIMD_SOCK, sizeof(STE_SIMD_SOCK) - 1);
-
- if (connect(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
- err = -errno;
- sap_error("connect to the socket failed: %s", strerror(-err));
- goto failed;
- }
-
- if (fcntl(sock, F_SETFL, O_NONBLOCK) > 0) {
- err = -errno;
- sap_error("setting up socket failed: %s", strerror(-err));
- goto failed;
- }
-
- simd_watch(sock, sap_data);
-
- return 0;
-
-failed:
- close(sock);
- return err;
-}
-
-void sap_connect_req(void *sap_device, uint16_t maxmsgsize)
-{
- DBG("sap_device %p maxmsgsize %u", sap_device, maxmsgsize);
-
- sap_info("connect request");
-
- if (simd_connect(sap_device) < 0) {
- sap_connect_rsp(sap_device, SAP_STATUS_CONNECTION_FAILED);
- return;
- }
-
- if (send_request(u8500.io, STE_START_SAP_REQ, NULL) < 0) {
- sap_connect_rsp(sap_device, SAP_STATUS_CONNECTION_FAILED);
- simd_close();
- }
-}
-
-void sap_disconnect_req(void *sap_device, uint8_t linkloss)
-{
- DBG("sap_device %p linkloss %u", sap_device, linkloss);
-
- sap_info("disconnect request %s", linkloss ? "by link loss" : "");
-
- if (u8500.state == STE_DISABLED) {
- sap_disconnect_rsp(sap_device);
- simd_close();
- return;
- }
-
- if (linkloss) {
- simd_close();
- return;
- }
-
- if (send_request(u8500.io, STE_END_SAP_REQ, NULL) < 0) {
- sap_disconnect_rsp(sap_device);
- return;
- }
-}
-
-void sap_transfer_apdu_req(void *sap_device, struct sap_parameter *param)
-{
- uint8_t result;
-
- SAP_VDBG("sap_device %p param %p", sap_device, param);
-
- if (u8500.state != STE_ENABLED) {
- result = get_sap_result(STE_SEND_APDU_MSG, STE_STATUS_FAILURE);
- sap_transfer_apdu_rsp(sap_device, result, NULL, 0);
- return;
- }
-
- if (send_request(u8500.io, STE_SEND_APDU_REQ, param) < 0)
- sap_transfer_apdu_rsp(sap_device, SAP_RESULT_ERROR_NO_DATA,
- NULL, 0);
-}
-
-void sap_transfer_atr_req(void *sap_device)
-{
- uint8_t result;
-
- DBG("sap_device %p", sap_device);
-
- if (u8500.state != STE_ENABLED) {
- result = get_sap_result(STE_GET_ATR_MSG, STE_STATUS_FAILURE);
- sap_transfer_atr_rsp(sap_device, result, NULL, 0);
- return;
- }
-
- if (send_request(u8500.io, STE_GET_ATR_REQ, NULL) < 0)
- sap_transfer_atr_rsp(sap_device, SAP_RESULT_ERROR_NO_DATA, NULL,
- 0);
-}
-
-void sap_power_sim_off_req(void *sap_device)
-{
- uint8_t result;
-
- DBG("sap_device %p", sap_device);
-
- if (u8500.state != STE_ENABLED) {
- result = get_sap_result(STE_POWER_OFF_MSG, STE_STATUS_FAILURE);
- sap_power_sim_off_rsp(sap_device, result);
- return;
- }
-
- if (send_request(u8500.io, STE_POWER_OFF_REQ, NULL) < 0)
- sap_power_sim_off_rsp(sap_device, SAP_RESULT_ERROR_NO_REASON);
-}
-
-void sap_power_sim_on_req(void *sap_device)
-{
- uint8_t result;
-
- DBG("sap_device %p", sap_device);
-
- if (u8500.state != STE_POWERED_OFF) {
- result = get_sap_result(STE_POWER_ON_MSG, STE_STATUS_FAILURE);
- sap_power_sim_on_rsp(sap_device, result);
- return;
- }
-
- if (send_request(u8500.io, STE_POWER_ON_REQ, NULL) < 0)
- sap_power_sim_on_rsp(sap_device, SAP_RESULT_ERROR_NO_REASON);
-}
-
-void sap_reset_sim_req(void *sap_device)
-{
- uint8_t result;
-
- DBG("sap_device %p", sap_device);
-
- if (u8500.state != STE_ENABLED) {
- result = get_sap_result(STE_RESET_MSG, STE_STATUS_FAILURE);
- sap_reset_sim_rsp(sap_device, result);
- return;
- }
-
- if (send_request(u8500.io, STE_RESET_REQ, NULL) < 0)
- sap_reset_sim_rsp(sap_device, SAP_RESULT_ERROR_NO_REASON);
-}
-
-void sap_transfer_card_reader_status_req(void *sap_device)
-{
- uint8_t result;
-
- DBG("sap_device %p", sap_device);
-
- if (u8500.state == STE_DISABLED) {
- result = get_sap_result(STE_GET_STATUS_MSG, STE_STATUS_FAILURE);
- sap_transfer_card_reader_status_rsp(sap_device, result, 0);
- return;
- }
-
- if (send_request(u8500.io, STE_GET_STATUS_REQ, NULL) < 0)
- sap_transfer_card_reader_status_rsp(sap_device,
- SAP_RESULT_ERROR_NO_DATA, 0);
-}
-
-void sap_set_transport_protocol_req(void *sap_device,
- struct sap_parameter *param)
-{
- DBG("sap_device %p", sap_device);
-
- sap_transport_protocol_rsp(sap_device, SAP_RESULT_NOT_SUPPORTED);
-}
-
-int sap_init(void)
-{
- u8500.state = STE_DISABLED;
- info("STE U8500 SAP driver initialized");
- return 0;
-}
-
-void sap_exit(void)
-{
-}
param->len = htons(SAP_PARAM_ID_MAX_MSG_SIZE_LEN);
put_be16(SAP_BUF_SIZE, ¶m->val);
size += PARAMETER_SIZE(SAP_PARAM_ID_MAX_MSG_SIZE_LEN);
-
- /* fall */
+ /* fall through */
default:
conn->state = SAP_STATE_DISCONNECTED;
/* Change state to connected after ongoing call ended */
sap_set_connected(server);
- /* fall */
+ /* fall through */
case SAP_STATE_CONNECTED:
case SAP_STATE_GRACEFUL_DISCONNECT:
memset(buf, 0, sizeof(buf));
}
scan->db = gatt_db_ref(db);
- scan->client = bt_gatt_client_ref(client);
+ scan->client = bt_gatt_client_clone(client);
bt_string_to_uuid(&scan_parameters_uuid, SCAN_PARAMETERS_UUID);
gatt_db_foreach_service(db, &scan_parameters_uuid,
uint16_t pathloss;
int16_t rssi;
GSList *uuids;
+ bool duplicate;
};
struct watch_client {
struct btd_adapter *adapter;
+ DBusMessage *msg;
char *owner;
guint watch;
struct discovery_filter *discovery_filter;
#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
bdaddr_t le_static_addr;
#endif
- int8_t bdaddr_type; /* address type */
+ uint8_t bdaddr_type; /* address type */
uint32_t dev_class; /* controller class of device */
char *name; /* controller device name */
char *short_name; /* controller short name */
uint32_t current_settings; /* current controller settings */
char *path; /* adapter object path */
+ uint16_t manufacturer; /* adapter manufacturer */
uint8_t major_class; /* configured major class */
uint8_t minor_class; /* configured minor class */
char *system_name; /* configured system name */
bool is_default; /* true if adapter is default one */
};
+typedef enum {
+ ADAPTER_AUTHORIZE_DISCONNECTED = 0,
+ ADAPTER_AUTHORIZE_CHECK_CONNECTED
+} adapter_authorize_type;
+
#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
enum {
DEINIT_6LOWPAN,
return MODE_UNKNOWN;
}
+static const char *adapter_dir(struct btd_adapter *adapter)
+{
+ static char dir[25];
+
+ if (adapter->bdaddr_type == BDADDR_LE_RANDOM) {
+ strcpy(dir, "static-");
+ ba2str(&adapter->bdaddr, dir + 7);
+ } else {
+ ba2str(&adapter->bdaddr, dir);
+ }
+
+ return dir;
+}
+
uint8_t btd_adapter_get_address_type(struct btd_adapter *adapter)
{
return adapter->bdaddr_type;
{
GKeyFile *key_file;
char filename[PATH_MAX];
- char address[18];
char *str;
gsize length = 0;
gboolean discoverable;
else
g_key_file_set_string(key_file, "General", "DefaultA2DPRole", "source");
#endif
- ba2str(&adapter->bdaddr, address);
- snprintf(filename, PATH_MAX, STORAGEDIR "/%s/settings", address);
+
+ snprintf(filename, PATH_MAX, STORAGEDIR "/%s/settings",
+ adapter_dir(adapter));
create_file(filename, S_IRUSR | S_IWUSR);
return;
if (adapter->pairable_timeout > 0)
- g_timeout_add_seconds(adapter->pairable_timeout,
+ adapter->pairable_timeout_id =
+ g_timeout_add_seconds(adapter->pairable_timeout,
pairable_timeout_handler, adapter);
}
#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
bool exact_match = false;
#endif
- char addr_str[18];
if (!adapter)
return NULL;
bacpy(&addr.bdaddr, dst);
addr.bdaddr_type = bdaddr_type;
- ba2str(dst, addr_str);
-
#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
list = g_slist_find_custom(adapter->devices, &addr,
device_addr_type_strict_cmp);
if (list) {
device = list->data;
exact_match = true;
- } else {
-#endif
- list = g_slist_find_custom(adapter->devices, &addr,
- device_addr_type_cmp);
- if (list) {
- device = list->data;
- }
}
-
+#else
+ list = g_slist_find_custom(adapter->devices, &addr,
+ device_addr_type_cmp);
+#endif
if (!list)
return NULL;
struct btd_adapter *adapter = user_data;
const struct mgmt_cp_start_discovery *rp = param;
- if (!rp) {
- error("Error ocurred in Scanning, rp is NULL");
- return;
- }
-
DBG("status 0x%02x", status);
if (length < sizeof(*rp)) {
const void *param, void *user_data)
{
struct btd_adapter *adapter = user_data;
+ struct watch_client *client = adapter->discovery_list->data;
const struct mgmt_cp_start_discovery *rp = param;
+ DBusMessage *reply;
DBG("status 0x%02x", status);
#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
- DBG("Discovery Type 0x%02x", rp->type);
+ DBG("status 0x%02x", status);
#endif
+ /* Is there are no clients the discovery must have been stopped while
+ * discovery command was pending.
+ */
+ if (!client) {
+ struct mgmt_cp_stop_discovery cp;
+
+ if (status != MGMT_STATUS_SUCCESS)
+ return;
+
+ /* Stop discovering as there are no clients left */
+ cp.type = rp->type;
+ mgmt_send(adapter->mgmt, MGMT_OP_STOP_DISCOVERY,
+ adapter->dev_id, sizeof(cp), &cp,
+ NULL, NULL, NULL);
+ return;
+ }
+
if (length < sizeof(*rp)) {
btd_error(adapter->dev_id,
"Wrong size of start discovery return parameters");
+ if (client->msg)
+ goto fail;
return;
}
else
adapter->filtered_discovery = false;
+ if (client->msg) {
+ reply = g_dbus_create_reply(client->msg,
+ DBUS_TYPE_INVALID);
+ g_dbus_send_message(dbus_conn, reply);
+ dbus_message_unref(client->msg);
+ client->msg = NULL;
+ }
+
if (adapter->discovering)
return;
g_dbus_emit_property_changed(dbus_conn, adapter->path,
ADAPTER_INTERFACE, "Discovering");
return;
-
#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
} else {
adapter->discovering = false;
g_dbus_emit_property_changed(dbus_conn, adapter->path,
ADAPTER_INTERFACE, "Discovering");
#endif
+ }
+fail:
+ /* Reply with an error if the first discovery has failed */
+ if (client->msg) {
+ reply = btd_error_busy(client->msg);
+ g_dbus_send_message(dbus_conn, reply);
+ g_dbus_remove_watch(dbus_conn, client->watch);
+ return;
}
/*
}
}
+static void invalidate_rssi_and_tx_power(gpointer a)
+{
+ struct btd_device *dev = a;
+
+ device_set_rssi(dev, 0);
+ device_set_tx_power(dev, 127);
+}
+
+static gboolean remove_temp_devices(gpointer user_data)
+{
+ struct btd_adapter *adapter = user_data;
+ GSList *l, *next;
+
+ DBG("%s", adapter->path);
+
+ adapter->temp_devices_timeout = 0;
+
+ for (l = adapter->devices; l != NULL; l = next) {
+ struct btd_device *dev = l->data;
+
+ next = g_slist_next(l);
+
+ if (device_is_temporary(dev) && !btd_device_is_connected(dev))
+ btd_adapter_remove_device(adapter, dev);
+ }
+
+ return FALSE;
+}
+
+static void discovery_cleanup(struct btd_adapter *adapter)
+{
+ GSList *l, *next;
+
+ adapter->discovery_type = 0x00;
+
+ if (adapter->discovery_idle_timeout > 0) {
+ g_source_remove(adapter->discovery_idle_timeout);
+ adapter->discovery_idle_timeout = 0;
+ }
+
+ if (adapter->temp_devices_timeout > 0) {
+ g_source_remove(adapter->temp_devices_timeout);
+ adapter->temp_devices_timeout = 0;
+ }
+
+ g_slist_free_full(adapter->discovery_found,
+ invalidate_rssi_and_tx_power);
+ adapter->discovery_found = NULL;
+
+ if (!adapter->devices)
+ return;
+
+ for (l = adapter->devices; l != NULL; l = next) {
+ struct btd_device *dev = l->data;
+
+ next = g_slist_next(l);
+
+ if (device_is_temporary(dev) && !device_is_connectable(dev))
+ btd_adapter_remove_device(adapter, dev);
+ }
+
+ adapter->temp_devices_timeout = g_timeout_add_seconds(TEMP_DEV_TIMEOUT,
+ remove_temp_devices, adapter);
+}
+
+static void discovery_free(void *user_data)
+{
+ struct watch_client *client = user_data;
+
+ if (client->watch)
+ g_dbus_remove_watch(dbus_conn, client->watch);
+
+ if (client->discovery_filter) {
+ free_discovery_filter(client->discovery_filter);
+ client->discovery_filter = NULL;
+ }
+
+ if (client->msg)
+ dbus_message_unref(client->msg);
+
+ g_free(client->owner);
+ g_free(client);
+}
+
+static void discovery_remove(struct watch_client *client)
+{
+ struct btd_adapter *adapter = client->adapter;
+
+ DBG("owner %s", client->owner);
+
+ adapter->set_filter_list = g_slist_remove(adapter->set_filter_list,
+ client);
+
+ adapter->discovery_list = g_slist_remove(adapter->discovery_list,
+ client);
+
+ discovery_free(client);
+
+ /*
+ * If there are other client discoveries in progress, then leave
+ * it active. If not, then make sure to stop the restart timeout.
+ */
+ if (adapter->discovery_list)
+ return;
+
+ discovery_cleanup(adapter);
+}
+
#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
static void stop_discovery_complete(uint8_t status, uint16_t length,
const void *param, void *user_data)
}
}
}
-
#else
static void stop_discovery_complete(uint8_t status, uint16_t length,
const void *param, void *user_data)
{
- struct btd_adapter *adapter = user_data;
+ struct watch_client *client = user_data;
+ struct btd_adapter *adapter = client->adapter;
+ DBusMessage *reply;
DBG("status 0x%02x", status);
- if (status == MGMT_STATUS_SUCCESS) {
- adapter->discovery_type = 0x00;
- adapter->discovery_enable = 0x00;
- adapter->filtered_discovery = false;
- adapter->no_scan_restart_delay = false;
- adapter->discovering = false;
- g_dbus_emit_property_changed(dbus_conn, adapter->path,
- ADAPTER_INTERFACE, "Discovering");
+ if (status != MGMT_STATUS_SUCCESS) {
+ if (client->msg) {
+ reply = btd_error_busy(client->msg);
+ g_dbus_send_message(dbus_conn, reply);
+ }
+ goto done;
+ }
- trigger_passive_scanning(adapter);
+ if (client->msg) {
+ reply = g_dbus_create_reply(client->msg, DBUS_TYPE_INVALID);
+ g_dbus_send_message(dbus_conn, reply);
}
+
+ adapter->discovery_type = 0x00;
+ adapter->discovery_enable = 0x00;
+ adapter->filtered_discovery = false;
+ adapter->no_scan_restart_delay = false;
+ adapter->discovering = false;
+ g_dbus_emit_property_changed(dbus_conn, adapter->path,
+ ADAPTER_INTERFACE, "Discovering");
+
+ trigger_passive_scanning(adapter);
+
+done:
+ discovery_remove(client);
}
#endif
return g_strcmp0(client->owner, sender);
}
-static void invalidate_rssi_and_tx_power(gpointer a)
-{
- struct btd_device *dev = a;
-
- device_set_rssi(dev, 0);
- device_set_tx_power(dev, 127);
-}
-
-static void discovery_cleanup(struct btd_adapter *adapter)
-{
- g_slist_free_full(adapter->discovery_found,
- invalidate_rssi_and_tx_power);
- adapter->discovery_found = NULL;
-}
-
-static gboolean remove_temp_devices(gpointer user_data)
-{
- struct btd_adapter *adapter = user_data;
- GSList *l, *next;
-
- DBG("%s", adapter->path);
-
- adapter->temp_devices_timeout = 0;
-
- for (l = adapter->devices; l != NULL; l = next) {
- struct btd_device *dev = l->data;
-
- next = g_slist_next(l);
-
- if (device_is_temporary(dev) && !btd_device_is_connected(dev))
- btd_adapter_remove_device(adapter, dev);
- }
-
- return FALSE;
-}
-
static gint g_strcmp(gconstpointer a, gconstpointer b)
{
return strcmp(a, b);
return true;
}
-static void update_discovery_filter(struct btd_adapter *adapter)
+static int update_discovery_filter(struct btd_adapter *adapter)
{
struct mgmt_cp_start_service_discovery *sd_cp;
if (discovery_filter_to_mgmt_cp(adapter, &sd_cp)) {
btd_error(adapter->dev_id,
"discovery_filter_to_mgmt_cp returned error");
- return;
+ return -ENOMEM;
}
/*
adapter->discovering != 0) {
DBG("filters were equal, deciding to not restart the scan.");
g_free(sd_cp);
- return;
+ return 0;
}
g_free(adapter->current_discovery_filter);
adapter->current_discovery_filter = sd_cp;
trigger_start_discovery(adapter, 0);
+
+ return -EINPROGRESS;
+}
+
+static int discovery_stop(struct watch_client *client)
+{
+ struct btd_adapter *adapter = client->adapter;
+ struct mgmt_cp_stop_discovery cp;
+
+ /* Check if there are more client discovering */
+ if (g_slist_next(adapter->discovery_list)) {
+ discovery_remove(client);
+ update_discovery_filter(adapter);
+ return 0;
+ }
+
+ /*
+ * In the idle phase of a discovery, there is no need to stop it
+ * and so it is enough to send out the signal and just return.
+ */
+ if (adapter->discovery_enable == 0x00) {
+ discovery_remove(client);
+ adapter->discovering = false;
+ g_dbus_emit_property_changed(dbus_conn, adapter->path,
+ ADAPTER_INTERFACE, "Discovering");
+
+ trigger_passive_scanning(adapter);
+
+ return 0;
+ }
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ adapter->disc_type = BT_DISC_TYPE_BREDR_ONLY;
+#endif
+ cp.type = adapter->discovery_type;
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ cp.type = 0x01;
+#endif
+ mgmt_send(adapter->mgmt, MGMT_OP_STOP_DISCOVERY,
+ adapter->dev_id, sizeof(cp), &cp,
+ stop_discovery_complete, client, NULL);
+
+ return -EINPROGRESS;
}
static void discovery_destroy(void *user_data)
static void discovery_disconnect(DBusConnection *conn, void *user_data)
{
struct watch_client *client = user_data;
- struct btd_adapter *adapter = client->adapter;
- struct mgmt_cp_stop_discovery cp;
DBG("owner %s", client->owner);
- adapter->set_filter_list = g_slist_remove(adapter->set_filter_list,
- client);
-
- adapter->discovery_list = g_slist_remove(adapter->discovery_list,
- client);
-
- /*
- * There is no need for extra cleanup of the client since that
- * will be done by the destroy callback.
- *
- * However in case this is the last client, the discovery in
- * the kernel needs to be disabled.
- */
- if (adapter->discovery_list) {
- update_discovery_filter(adapter);
- return;
- }
-
- /*
- * In the idle phase of a discovery, there is no need to stop it
- * and so it is enough to send out the signal and just return.
- */
- if (adapter->discovery_enable == 0x00) {
- adapter->discovering = false;
- g_dbus_emit_property_changed(dbus_conn, adapter->path,
- ADAPTER_INTERFACE, "Discovering");
-
- trigger_passive_scanning(adapter);
- return;
- }
-
- cp.type = adapter->discovery_type;
-
- mgmt_send(adapter->mgmt, MGMT_OP_STOP_DISCOVERY,
- adapter->dev_id, sizeof(cp), &cp,
- stop_discovery_complete, adapter, NULL);
+ discovery_stop(client);
}
/*
const char *sender = dbus_message_get_sender(msg);
struct watch_client *client;
bool is_discovering;
+ int err;
DBG("sender %s", sender);
* and trigger scan.
*/
if (client) {
+ if (client->msg)
+ return btd_error_busy(msg);
+
adapter->set_filter_list = g_slist_remove(
adapter->set_filter_list, client);
adapter->discovery_list = g_slist_prepend(
adapter->discovery_list, client);
- update_discovery_filter(adapter);
- return dbus_message_new_method_return(msg);
+ goto done;
}
client = g_new0(struct watch_client, 1);
client->discovery_filter = NULL;
client->watch = g_dbus_add_disconnect_watch(dbus_conn, sender,
discovery_disconnect, client,
- discovery_destroy);
+ NULL);
adapter->discovery_list = g_slist_prepend(adapter->discovery_list,
client);
-#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
- adapter->disc_type = BT_DISC_TYPE_BREDR_ONLY;
-#endif
-
+done:
/*
* Just trigger the discovery here. In case an already running
* discovery in idle phase exists, it will be restarted right
* away.
*/
- update_discovery_filter(adapter);
+ err = update_discovery_filter(adapter);
+ if (!err)
+ return dbus_message_new_method_return(msg);
- return dbus_message_new_method_return(msg);
+ /* If the discovery has to be started wait it complete to reply */
+ if (err == -EINPROGRESS) {
+ client->msg = dbus_message_ref(msg);
+ return NULL;
+ }
+
+ return btd_error_failed(msg, strerror(-err));
}
-static bool parse_uuids(DBusMessageIter *value, GSList **uuids)
+static bool parse_uuids(DBusMessageIter *value, struct discovery_filter *filter)
{
DBusMessageIter arriter;
bt_uuid_to_uuid128(&uuid, &u128);
bt_uuid_to_string(&u128, uuidstr, sizeof(uuidstr));
- *uuids = g_slist_prepend(*uuids, strdup(uuidstr));
+ filter->uuids = g_slist_prepend(filter->uuids, strdup(uuidstr));
dbus_message_iter_next(&arriter);
}
return true;
}
-static bool parse_rssi(DBusMessageIter *value, int16_t *rssi)
+static bool parse_rssi(DBusMessageIter *value, struct discovery_filter *filter)
{
if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_INT16)
return false;
- dbus_message_iter_get_basic(value, rssi);
+ dbus_message_iter_get_basic(value, &filter->rssi);
/* -127 <= RSSI <= +20 (spec V4.2 [Vol 2, Part E] 7.7.65.2) */
- if (*rssi > 20 || *rssi < -127)
+ if (filter->rssi > 20 || filter->rssi < -127)
return false;
return true;
}
-static bool parse_pathloss(DBusMessageIter *value, uint16_t *pathloss)
+static bool parse_pathloss(DBusMessageIter *value,
+ struct discovery_filter *filter)
{
if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_UINT16)
return false;
- dbus_message_iter_get_basic(value, pathloss);
+ dbus_message_iter_get_basic(value, &filter->pathloss);
/* pathloss filter must be smaller that PATHLOSS_MAX */
- if (*pathloss > PATHLOSS_MAX)
+ if (filter->pathloss > PATHLOSS_MAX)
return false;
return true;
}
-static bool parse_transport(DBusMessageIter *value, uint8_t *transport)
+static bool parse_transport(DBusMessageIter *value,
+ struct discovery_filter *filter)
{
char *transport_str;
dbus_message_iter_get_basic(value, &transport_str);
if (!strcmp(transport_str, "bredr"))
- *transport = SCAN_TYPE_BREDR;
+ filter->type = SCAN_TYPE_BREDR;
else if (!strcmp(transport_str, "le"))
- *transport = SCAN_TYPE_LE;
- else if (!strcmp(transport_str, "auto"))
- *transport = SCAN_TYPE_DUAL;
- else
+ filter->type = SCAN_TYPE_LE;
+ else if (strcmp(transport_str, "auto"))
return false;
return true;
}
-static bool parse_discovery_filter_entry(char *key, DBusMessageIter *value,
- struct discovery_filter *filter)
+static bool parse_duplicate_data(DBusMessageIter *value,
+ struct discovery_filter *filter)
{
- if (!strcmp("UUIDs", key))
- return parse_uuids(value, &filter->uuids);
+ if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_BOOLEAN)
+ return false;
- if (!strcmp("RSSI", key))
- return parse_rssi(value, &filter->rssi);
+ dbus_message_iter_get_basic(value, &filter->duplicate);
- if (!strcmp("Pathloss", key))
- return parse_pathloss(value, &filter->pathloss);
+ return true;
+}
- if (!strcmp("Transport", key))
- return parse_transport(value, &filter->type);
+struct filter_parser {
+ const char *name;
+ bool (*func)(DBusMessageIter *iter, struct discovery_filter *filter);
+} parsers[] = {
+ { "UUIDs", parse_uuids },
+ { "RSSI", parse_rssi },
+ { "Pathloss", parse_pathloss },
+ { "Transport", parse_transport },
+ { "DuplicateData", parse_duplicate_data },
+ { }
+};
+
+static bool parse_discovery_filter_entry(char *key, DBusMessageIter *value,
+ struct discovery_filter *filter)
+{
+ struct filter_parser *parser;
+
+ for (parser = parsers; parser && parser->name; parser++) {
+ if (!strcmp(parser->name, key))
+ return parser->func(value, filter);
+ }
DBG("Unknown key parameter: %s!\n", key);
return false;
(*filter)->pathloss = DISTANCE_VAL_INVALID;
(*filter)->rssi = DISTANCE_VAL_INVALID;
(*filter)->type = get_scan_type(adapter);
+ (*filter)->duplicate = false;
dbus_message_iter_init(msg, &iter);
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
(*filter)->rssi != DISTANCE_VAL_INVALID)
goto invalid_args;
- DBG("filtered discovery params: transport: %d rssi: %d pathloss: %d",
- (*filter)->type, (*filter)->rssi, (*filter)->pathloss);
+ DBG("filtered discovery params: transport: %d rssi: %d pathloss: %d "
+ " duplicate data: %s ", (*filter)->type, (*filter)->rssi,
+ (*filter)->pathloss, (*filter)->duplicate ? "true" : "false");
return true;
adapter->set_filter_list = g_slist_remove(
adapter->set_filter_list,
client);
- g_free(client->owner);
- g_free(client);
+ discovery_free(client);
DBG("successfully cleared pre-set filter");
} else if (discovery_filter) {
/* Client pre-setting his filter for first time */
client->discovery_filter = discovery_filter;
client->watch = g_dbus_add_disconnect_watch(dbus_conn, sender,
discovery_disconnect, client,
- discovery_destroy);
+ NULL);
adapter->set_filter_list = g_slist_prepend(
adapter->set_filter_list, client);
{
struct btd_adapter *adapter = user_data;
const char *sender = dbus_message_get_sender(msg);
- struct mgmt_cp_stop_discovery cp;
struct watch_client *client;
GSList *list;
+ int err;
DBG("sender %s", sender);
return btd_error_not_ready(msg);
list = g_slist_find_custom(adapter->discovery_list, sender,
- compare_sender);
+ compare_sender);
if (!list)
return btd_error_failed(msg, "No discovery started");
client = list->data;
-#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
- adapter->disc_type = BT_DISC_TYPE_BREDR_ONLY;
-#endif
- cp.type = adapter->discovery_type;
-
- /*
- * The destroy function will cleanup the client information and
- * also remove it from the list of discovery clients.
- */
- g_dbus_remove_watch(dbus_conn, client->watch);
-
- if (adapter->discovery_list) {
- update_discovery_filter(adapter);
- return dbus_message_new_method_return(msg);
- }
-
- /*
- * In the idle phase of a discovery, there is no need to stop it
- * and so it is enough to send out the signal and just return.
- */
- if (adapter->discovery_enable == 0x00) {
- adapter->discovering = false;
- g_dbus_emit_property_changed(dbus_conn, adapter->path,
- ADAPTER_INTERFACE, "Discovering");
-
- trigger_passive_scanning(adapter);
+ if (client->msg)
+ return btd_error_busy(msg);
- return dbus_message_new_method_return(msg);
+ err = discovery_stop(client);
+ switch (err) {
+ case 0:
+ return dbus_message_new_method_return(msg);
+ case -EINPROGRESS:
+ client->msg = dbus_message_ref(msg);
+ return NULL;
+ default:
+ return btd_error_failed(msg, strerror(-err));
}
-#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
- cp.type = 0x01;
-#endif
- mgmt_send(adapter->mgmt, MGMT_OP_STOP_DISCOVERY,
- adapter->dev_id, sizeof(cp), &cp,
- stop_discovery_complete, adapter, NULL);
-
- return dbus_message_new_method_return(msg);
}
static gboolean property_get_address(const GDBusPropertyTable *property,
return TRUE;
}
+static gboolean property_get_address_type(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *user_data)
+{
+ struct btd_adapter *adapter = user_data;
+ const char *str;
+
+ if ((adapter->current_settings & MGMT_SETTING_LE) &&
+ (adapter->bdaddr_type == BDADDR_LE_RANDOM))
+ str = "random";
+ else
+ str = "public";
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &str);
+
+ return TRUE;
+}
+
#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
static gboolean property_get_le_address(const GDBusPropertyTable *property,
DBusMessageIter *iter, void *user_data)
g_hash_table_foreach(uuids, iter_append_uuid, &entry);
dbus_message_iter_close_container(iter, &entry);
- g_hash_table_destroy(uuids);
+ g_hash_table_destroy(uuids);
+
+ return TRUE;
+}
+
+static gboolean property_exists_modalias(const GDBusPropertyTable *property,
+ void *user_data)
+{
+ struct btd_adapter *adapter = user_data;
+
+ return adapter->modalias ? TRUE : FALSE;
+}
+
+static gboolean property_get_modalias(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *user_data)
+{
+ struct btd_adapter *adapter = user_data;
+ const char *str = adapter->modalias ? : "";
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &str);
+
+ return TRUE;
+}
+
+static int device_path_cmp(gconstpointer a, gconstpointer b)
+{
+ const struct btd_device *device = a;
+ const char *path = b;
+ const char *dev_path = device_get_path(device);
+
+ return strcasecmp(dev_path, path);
+}
+
+static DBusMessage *remove_device(DBusConnection *conn,
+ DBusMessage *msg, void *user_data)
+{
+ struct btd_adapter *adapter = user_data;
+ struct btd_device *device;
+ const char *path;
+ GSList *list;
+
+ if (dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID) == FALSE)
+ return btd_error_invalid_args(msg);
+
+ list = g_slist_find_custom(adapter->devices, path, device_path_cmp);
+ if (!list)
+ return btd_error_does_not_exist(msg);
+
+ if (!(adapter->current_settings & MGMT_SETTING_POWERED))
+ return btd_error_not_ready(msg);
+
+ device = list->data;
+
+ btd_device_set_temporary(device, true);
+
+ if (!btd_device_is_connected(device)) {
+ btd_adapter_remove_device(adapter, device);
+ return dbus_message_new_method_return(msg);
+ }
- return TRUE;
+ device_request_disconnect(device, msg);
+
+ return NULL;
}
-static gboolean property_exists_modalias(const GDBusPropertyTable *property,
- void *user_data)
+static DBusMessage *get_discovery_filters(DBusConnection *conn,
+ DBusMessage *msg, void *user_data)
{
- struct btd_adapter *adapter = user_data;
+ DBusMessage *reply;
+ DBusMessageIter iter, array;
+ struct filter_parser *parser;
- return adapter->modalias ? TRUE : FALSE;
-}
+ reply = dbus_message_new_method_return(msg);
-static gboolean property_get_modalias(const GDBusPropertyTable *property,
- DBusMessageIter *iter, void *user_data)
-{
- struct btd_adapter *adapter = user_data;
- const char *str = adapter->modalias ? : "";
+ dbus_message_iter_init_append(reply, &iter);
- dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &str);
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_TYPE_STRING_AS_STRING, &array);
- return TRUE;
-}
+ for (parser = parsers; parser && parser->name; parser++) {
+ dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING,
+ &parser->name);
+ }
-static int device_path_cmp(gconstpointer a, gconstpointer b)
-{
- const struct btd_device *device = a;
- const char *path = b;
- const char *dev_path = device_get_path(device);
+ dbus_message_iter_close_container(&iter, &array);
- return strcasecmp(dev_path, path);
+ return reply;
}
+
#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
static DBusMessage *adapter_unpair_device(DBusConnection *conn,
DBusMessage *msg, void *user_data)
}
#endif
-static DBusMessage *remove_device(DBusConnection *conn,
- DBusMessage *msg, void *user_data)
-{
- struct btd_adapter *adapter = user_data;
- struct btd_device *device;
- const char *path;
- GSList *list;
-
- if (dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
- DBUS_TYPE_INVALID) == FALSE)
- return btd_error_invalid_args(msg);
-
- list = g_slist_find_custom(adapter->devices, path, device_path_cmp);
- if (!list)
- return btd_error_does_not_exist(msg);
-
- if (!(adapter->current_settings & MGMT_SETTING_POWERED))
- return btd_error_not_ready(msg);
-
- device = list->data;
-
- btd_device_set_temporary(device, true);
-
- if (!btd_device_is_connected(device)) {
- btd_adapter_remove_device(adapter, device);
- return dbus_message_new_method_return(msg);
- }
-
- device_request_disconnect(device, msg);
-
- return NULL;
-}
-
static const GDBusMethodTable adapter_methods[] = {
- { GDBUS_METHOD("StartDiscovery", NULL, NULL, start_discovery) },
+ { GDBUS_ASYNC_METHOD("StartDiscovery", NULL, NULL, start_discovery) },
{ GDBUS_METHOD("SetDiscoveryFilter",
GDBUS_ARGS({ "properties", "a{sv}" }), NULL,
set_discovery_filter) },
- { GDBUS_METHOD("StopDiscovery", NULL, NULL, stop_discovery) },
+ { GDBUS_ASYNC_METHOD("StopDiscovery", NULL, NULL, stop_discovery) },
#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
{ GDBUS_METHOD("StartCustomDiscovery",
GDBUS_ARGS({ "type", "s" }), NULL,
GDBUS_ARGS({"def_tx_octets", "q" }, { "def_tx_time", "q" }),
le_read_host_suggested_default_data_length)},
#endif
+ { GDBUS_METHOD("GetDiscoveryFilters", NULL,
+ GDBUS_ARGS({ "filters", "as" }),
+ get_discovery_filters) },
{ }
};
static const GDBusPropertyTable adapter_properties[] = {
{ "Address", "s", property_get_address },
+ { "AddressType", "s", property_get_address_type },
{ "Name", "s", property_get_name },
{ "Alias", "s", property_get_alias, property_set_alias },
{ "Class", "u", property_get_class },
{
char filename[PATH_MAX];
GKeyFile *key_file;
- char address[18];
char *str_irk;
int ret;
- ba2str(&adapter->bdaddr, address);
- snprintf(filename, PATH_MAX, STORAGEDIR "/%s/identity", address);
+ snprintf(filename, PATH_MAX, STORAGEDIR "/%s/identity",
+ adapter_dir(adapter));
key_file = g_key_file_new();
g_key_file_load_from_file(key_file, filename, 0, NULL);
str_irk = g_key_file_get_string(key_file, "General",
"IdentityResolvingKey", NULL);
if (!str_irk) {
- info("No IRK for %s, creating new IRK", address);
+ info("No IRK stored");
ret = generate_and_write_irk(irk, key_file, filename);
g_key_file_free(key_file);
return ret;
static void load_devices(struct btd_adapter *adapter)
{
char dirname[PATH_MAX];
- char srcaddr[18];
GSList *keys = NULL;
GSList *ltks = NULL;
GSList *irks = NULL;
DIR *dir;
struct dirent *entry;
- ba2str(&adapter->bdaddr, srcaddr);
-
- snprintf(dirname, PATH_MAX, STORAGEDIR "/%s", srcaddr);
+ snprintf(dirname, PATH_MAX, STORAGEDIR "/%s", adapter_dir(adapter));
dir = opendir(dirname);
if (!dir) {
}
}
#endif
- snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s/info", srcaddr,
- entry->d_name);
+ snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s/info",
+ adapter_dir(adapter), entry->d_name);
key_file = g_key_file_new();
g_key_file_load_from_file(key_file, filename, 0, NULL);
DBG("%p", adapter);
+ if (adapter->pairable_timeout_id > 0) {
+ g_source_remove(adapter->pairable_timeout_id);
+ adapter->pairable_timeout_id = 0;
+ }
+
+ if (adapter->passive_scan_timeout > 0) {
+ g_source_remove(adapter->passive_scan_timeout);
+ adapter->passive_scan_timeout = 0;
+ }
+
if (adapter->load_ltks_timeout > 0)
g_source_remove(adapter->load_ltks_timeout);
{
GKeyFile *key_file;
char filename[PATH_MAX];
- char address[18];
struct stat st;
GError *gerr = NULL;
#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
char *str;
#endif
- ba2str(&adapter->bdaddr, address);
key_file = g_key_file_new();
- snprintf(filename, PATH_MAX, STORAGEDIR "/%s/settings", address);
+ snprintf(filename, PATH_MAX, STORAGEDIR "/%s/settings",
+ adapter_dir(adapter));
if (stat(filename, &st) < 0) {
convert_config(adapter, filename, key_file);
g_slist_free(adapter->devices);
adapter->devices = NULL;
+ discovery_cleanup(adapter);
+
unload_drivers(adapter);
#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
return got_match;
}
+static void filter_duplicate_data(void *data, void *user_data)
+{
+ struct watch_client *client = data;
+ bool *duplicate = user_data;
+
+ if (*duplicate || !client->discovery_filter)
+ return;
+
+ *duplicate = client->discovery_filter->duplicate;
+}
+
#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
static void update_found_devices(struct btd_adapter *adapter,
const bdaddr_t *bdaddr,
uint8_t allow_report;
#endif
char addr[18];
+ bool duplicate = false;
memset(&eir_data, 0, sizeof(eir_data));
eir_parse(&eir_data, data, data_len);
device_store_cached_name(dev, eir_data.name);
/*
- * If no client has requested discovery, then only update
- * already paired devices (skip temporary ones).
+ * Only skip devices that are not connected, are temporary and there
+ * is no active discovery session ongoing.
*/
#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
if (device_is_temporary(dev) && adapter->discovery_list == NULL &&
device_set_last_addr_type(dev, bdaddr_type);
device_set_ipsp_connected(dev, FALSE, NULL);
#else
- if (device_is_temporary(dev) && !adapter->discovery_list) {
+ if (!btd_device_is_connected(dev) && (device_is_temporary(dev) &&
+ !adapter->discovery_list)) {
eir_data_free(&eir_data);
return;
}
device_add_eir_uuids(dev, eir_data.services);
+ if (adapter->discovery_list)
+ g_slist_foreach(adapter->discovery_list, filter_duplicate_data,
+ &duplicate);
#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
if (eir_data.flags != 0)
device_set_remote_feature_flag(dev, eir_data.flags);
#endif
if (eir_data.msd_list) {
- device_set_manufacturer_data(dev, eir_data.msd_list);
+ device_set_manufacturer_data(dev, eir_data.msd_list, duplicate);
adapter_msd_notify(adapter, dev, eir_data.msd_list);
}
if (eir_data.sd_list)
- device_set_service_data(dev, eir_data.sd_list);
+ device_set_service_data(dev, eir_data.sd_list, duplicate);
if (bdaddr_type != BDADDR_BREDR)
device_set_flags(dev, eir_data.flags);
cancel_passive_scanning(adapter);
- while (adapter->set_filter_list) {
- struct watch_client *client;
-
- client = adapter->set_filter_list->data;
-
- /* g_dbus_remove_watch will remove the client from the
- * adapter's list and free it using the discovery_destroy
- * function.
- */
- g_dbus_remove_watch(dbus_conn, client->watch);
- }
-
- while (adapter->discovery_list) {
- struct watch_client *client;
+ g_slist_free_full(adapter->set_filter_list, discovery_free);
+ adapter->set_filter_list = NULL;
- client = adapter->discovery_list->data;
+ g_slist_free_full(adapter->discovery_list, discovery_free);
+ adapter->discovery_list = NULL;
- /* g_dbus_remove_watch will remove the client from the
- * adapter's list and free it using the discovery_destroy
- * function.
- */
- g_dbus_remove_watch(dbus_conn, client->watch);
- }
+ discovery_cleanup(adapter);
adapter->filtered_discovery = false;
adapter->no_scan_restart_delay = false;
}
static int adapter_authorize(struct btd_adapter *adapter, const bdaddr_t *dst,
- const char *uuid, service_auth_cb cb,
- void *user_data)
+ const char *uuid,
+ adapter_authorize_type check_for_connection,
+ service_auth_cb cb, void *user_data)
{
struct service_auth *auth;
struct btd_device *device;
}
/* Device connected? */
- if (!g_slist_find(adapter->connections, device))
+ if (check_for_connection && !g_slist_find(adapter->connections, device))
btd_error(adapter->dev_id,
"Authorization request for non-connected device!?");
auth->device = device;
auth->adapter = adapter;
auth->id = ++id;
- auth->svc_id = device_wait_for_svc_complete(device, svc_complete, auth);
+ if (check_for_connection)
+ auth->svc_id = device_wait_for_svc_complete(device, svc_complete, auth);
+ else {
+ if (adapter->auth_idle_id == 0)
+ adapter->auth_idle_id = g_idle_add(process_auth_queue, adapter);
+ }
g_queue_push_tail(adapter->auths, auth);
if (!adapter)
return 0;
- return adapter_authorize(adapter, dst, uuid, cb, user_data);
+ return adapter_authorize(adapter, dst, uuid,
+ ADAPTER_AUTHORIZE_CHECK_CONNECTED, cb, user_data);
}
for (l = adapters; l != NULL; l = g_slist_next(l)) {
adapter = l->data;
- id = adapter_authorize(adapter, dst, uuid, cb, user_data);
+ id = adapter_authorize(adapter, dst, uuid,
+ ADAPTER_AUTHORIZE_CHECK_CONNECTED, cb, user_data);
if (id != 0)
return id;
}
return 0;
}
+guint btd_request_authorization_cable_configured(const bdaddr_t *src, const bdaddr_t *dst,
+ const char *uuid, service_auth_cb cb,
+ void *user_data)
+{
+ struct btd_adapter *adapter;
+
+ if (bacmp(src, BDADDR_ANY) == 0)
+ return 0;
+
+ adapter = adapter_find(src);
+ if (!adapter)
+ return 0;
+
+ return adapter_authorize(adapter, dst, uuid,
+ ADAPTER_AUTHORIZE_DISCONNECTED, cb, user_data);
+}
+
static struct service_auth *find_authorization(guint id)
{
GSList *l;
struct btd_device *device, const uint8_t *key,
uint8_t type, uint8_t pin_length)
{
- char adapter_addr[18];
char device_addr[18];
char filename[PATH_MAX];
GKeyFile *key_file;
char *str;
int i;
- ba2str(btd_adapter_get_address(adapter), adapter_addr);
ba2str(device_get_address(device), device_addr);
- snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s/info", adapter_addr,
- device_addr);
+ snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s/info",
+ adapter_dir(adapter), device_addr);
key_file = g_key_file_new();
g_key_file_load_from_file(key_file, filename, 0, NULL);
bonding_complete(adapter, &addr->bdaddr, addr->type, 0);
}
-static void store_longtermkey(const bdaddr_t *local, const bdaddr_t *peer,
- uint8_t bdaddr_type, const unsigned char *key,
- uint8_t master, uint8_t authenticated,
- uint8_t enc_size, uint16_t ediv,
- uint64_t rand)
+static void store_longtermkey(struct btd_adapter *adapter, const bdaddr_t *peer,
+ uint8_t bdaddr_type, const unsigned char *key,
+ uint8_t master, uint8_t authenticated,
+ uint8_t enc_size, uint16_t ediv,
+ uint64_t rand)
{
const char *group = master ? "LongTermKey" : "SlaveLongTermKey";
- char adapter_addr[18];
char device_addr[18];
char filename[PATH_MAX];
GKeyFile *key_file;
return;
}
- ba2str(local, adapter_addr);
ba2str(peer, device_addr);
- snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s/info", adapter_addr,
- device_addr);
+ snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s/info",
+ adapter_dir(adapter), device_addr);
key_file = g_key_file_new();
g_key_file_load_from_file(key_file, filename, 0, NULL);
g_key_file_set_string(key_file, group, "Key", key_str);
g_key_file_set_integer(key_file, group, "Authenticated",
- authenticated);
+ authenticated);
g_key_file_set_integer(key_file, group, "EncSize", enc_size);
g_key_file_set_integer(key_file, group, "EDiv", ediv);
if (persistent) {
const struct mgmt_ltk_info *key = &ev->key;
- const bdaddr_t *bdaddr = btd_adapter_get_address(adapter);
uint16_t ediv;
uint64_t rand;
#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
device_get_tizen_addr(device, addr->type, &t_addr);
- store_longtermkey(bdaddr, &t_addr.bdaddr, t_addr.bdaddr_type,
+ store_longtermkey(adapter, &t_addr.bdaddr, t_addr.bdaddr_type,
key->val, key->master, key->type,
key->enc_size, ediv, rand);
#else
- store_longtermkey(bdaddr, &key->addr.bdaddr,
+ store_longtermkey(adapter, &key->addr.bdaddr,
key->addr.type, key->val, key->master,
key->type, key->enc_size, ediv, rand);
#endif
bonding_complete(adapter, &addr->bdaddr, addr->type, 0);
}
-static void store_csrk(const bdaddr_t *local, const bdaddr_t *peer,
- uint8_t bdaddr_type, const unsigned char *key,
- uint32_t counter, uint8_t type)
+static void store_csrk(struct btd_adapter *adapter, const bdaddr_t *peer,
+ uint8_t bdaddr_type, const unsigned char *key,
+ uint32_t counter, uint8_t type)
{
const char *group;
- char adapter_addr[18];
char device_addr[18];
char filename[PATH_MAX];
GKeyFile *key_file;
return;
}
- ba2str(local, adapter_addr);
ba2str(peer, device_addr);
- snprintf(filename, sizeof(filename), STORAGEDIR "/%s/%s/info",
- adapter_addr, device_addr);
+ snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s/info",
+ adapter_dir(adapter), device_addr);
key_file = g_key_file_new();
g_key_file_load_from_file(key_file, filename, 0, NULL);
const struct mgmt_addr_info *addr = &ev->key.addr;
const struct mgmt_csrk_info *key = &ev->key;
struct btd_adapter *adapter = user_data;
- const bdaddr_t *bdaddr = btd_adapter_get_address(adapter);
struct btd_device *device;
char dst[18];
#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
device_get_tizen_addr(device, addr->type, &t_addr);
- store_csrk(bdaddr, &t_addr.bdaddr, t_addr.bdaddr_type, key->val, 0,
+ store_csrk(adapter, &t_addr.bdaddr, t_addr.bdaddr_type, key->val, 0,
key->type);
#else
- store_csrk(bdaddr, &key->addr.bdaddr, key->addr.type, key->val, 0,
+ store_csrk(adapter, &key->addr.bdaddr, key->addr.type, key->val, 0,
key->type);
#endif
static void store_irk(struct btd_adapter *adapter, const bdaddr_t *peer,
uint8_t bdaddr_type, const unsigned char *key)
{
- char adapter_addr[18];
char device_addr[18];
char filename[PATH_MAX];
GKeyFile *key_file;
size_t length = 0;
int i;
- ba2str(&adapter->bdaddr, adapter_addr);
ba2str(peer, device_addr);
- snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s/info", adapter_addr,
- device_addr);
+ snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s/info",
+ adapter_dir(adapter), device_addr);
key_file = g_key_file_new();
g_key_file_load_from_file(key_file, filename, 0, NULL);
device = btd_adapter_get_device(adapter, &ev->rpa,
BDADDR_LE_RANDOM);
#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
+ device = btd_adapter_get_device(adapter, &ev->rpa,
+ BDADDR_LE_RANDOM);
duplicate = btd_adapter_find_device(adapter, &addr->bdaddr,
- addr->type);
+ addr->type);
if (duplicate == device)
duplicate = NULL;
#else
uint16_t max_interval, uint16_t latency,
uint16_t timeout)
{
- char adapter_addr[18];
char device_addr[18];
char filename[PATH_MAX];
GKeyFile *key_file;
char *store_data;
size_t length = 0;
- ba2str(&adapter->bdaddr, adapter_addr);
ba2str(peer, device_addr);
DBG("");
- snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s/info", adapter_addr,
- device_addr);
+ snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s/info",
+ adapter_dir(adapter), device_addr);
key_file = g_key_file_new();
g_key_file_load_from_file(key_file, filename, 0, NULL);
return -EINVAL;
}
- if (g_dbus_get_flags() & G_DBUS_FLAG_ENABLE_EXPERIMENTAL) {
- /* Don't start advertising managers on non-LE controllers. */
- if (adapter->supported_settings & MGMT_SETTING_LE) {
- adapter->adv_manager = btd_adv_manager_new(adapter);
- } else {
- btd_info(adapter->dev_id,
- "LEAdvertisingManager skipped, LE unavailable");
- }
- }
+ /* Don't start advertising managers on non-LE controllers. */
+ if (adapter->supported_settings & MGMT_SETTING_LE)
+ adapter->adv_manager = btd_adv_manager_new(adapter);
+ else
+ btd_info(adapter->dev_id,
+ "LEAdvertisingManager skipped, LE unavailable");
db = btd_gatt_database_get_db(adapter->database);
adapter->db_id = gatt_db_register(db, services_modified,
static void remove_keys(struct btd_adapter *adapter,
struct btd_device *device, uint8_t type)
{
- char adapter_addr[18];
char device_addr[18];
char filename[PATH_MAX];
GKeyFile *key_file;
gsize length = 0;
char *str;
- ba2str(btd_adapter_get_address(adapter), adapter_addr);
ba2str(device_get_address(device), device_addr);
#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
ba2str(device_get_rpa(device), device_addr);
#endif
- snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s/info", adapter_addr,
- device_addr);
+ snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s/info",
+ adapter_dir(adapter), device_addr);
key_file = g_key_file_new();
g_key_file_load_from_file(key_file, filename, 0, NULL);
device_set_unpaired(device, ev->addr.type);
}
+static void clear_devices_complete(uint8_t status, uint16_t length,
+ const void *param, void *user_data)
+{
+ if (status != MGMT_STATUS_SUCCESS) {
+ error("Failed to clear devices: %s (0x%02x)",
+ mgmt_errstr(status), status);
+ return;
+ }
+}
+
+static int clear_devices(struct btd_adapter *adapter)
+{
+ struct mgmt_cp_remove_device cp;
+
+ if (!kernel_conn_control)
+ return 0;
+
+ memset(&cp, 0, sizeof(cp));
+
+ DBG("sending clear devices command for index %u", adapter->dev_id);
+
+ if (mgmt_send(adapter->mgmt, MGMT_OP_REMOVE_DEVICE,
+ adapter->dev_id, sizeof(cp), &cp,
+ clear_devices_complete, adapter, NULL) > 0)
+ return 0;
+
+ btd_error(adapter->dev_id, "Failed to clear devices for index %u",
+ adapter->dev_id);
+
+ return -EIO;
+}
+
+static bool get_static_addr(struct btd_adapter *adapter)
+{
+ struct bt_crypto *crypto;
+ GKeyFile *file;
+ char **addrs;
+ char mfg[7];
+ char *str;
+ bool ret;
+ gsize len, i;
+
+ snprintf(mfg, sizeof(mfg), "0x%04x", adapter->manufacturer);
+
+ file = g_key_file_new();
+ g_key_file_load_from_file(file, STORAGEDIR "/addresses", 0, NULL);
+ addrs = g_key_file_get_string_list(file, "Static", mfg, &len, NULL);
+ if (addrs) {
+ for (i = 0; i < len; i++) {
+ bdaddr_t addr;
+
+ str2ba(addrs[i], &addr);
+ if (adapter_find(&addr))
+ continue;
+
+ /* Usable address found in list */
+ bacpy(&adapter->bdaddr, &addr);
+ adapter->bdaddr_type = BDADDR_LE_RANDOM;
+ ret = true;
+ goto done;
+ }
+
+ len++;
+ addrs = g_renew(char *, addrs, len + 1);
+ } else {
+ len = 1;
+ addrs = g_new(char *, len + 1);
+ }
+
+ /* Initialize slot for new address */
+ addrs[len - 1] = g_malloc(18);
+ addrs[len] = NULL;
+
+ crypto = bt_crypto_new();
+ if (!crypto) {
+ error("Failed to open crypto");
+ ret = false;
+ goto done;
+ }
+
+ ret = bt_crypto_random_bytes(crypto, &adapter->bdaddr,
+ sizeof(adapter->bdaddr));
+ if (!ret) {
+ error("Failed to generate static address");
+ bt_crypto_unref(crypto);
+ goto done;
+ }
+
+ bt_crypto_unref(crypto);
+
+ adapter->bdaddr.b[5] |= 0xc0;
+ adapter->bdaddr_type = BDADDR_LE_RANDOM;
+
+ ba2str(&adapter->bdaddr, addrs[len - 1]);
+
+ g_key_file_set_string_list(file, "Static", mfg,
+ (const char **)addrs, len);
+
+ str = g_key_file_to_data(file, &len, NULL);
+ g_file_set_contents(STORAGEDIR "/addresses", str, len, NULL);
+ g_free(str);
+
+ ret = true;
+
+done:
+ g_key_file_free(file);
+ g_strfreev(addrs);
+
+ return ret;
+}
+
+static bool set_static_addr(struct btd_adapter *adapter)
+{
+ struct mgmt_cp_set_static_address cp;
+
+ /* dual-mode adapters must have a public address */
+ if (adapter->supported_settings & MGMT_SETTING_BREDR)
+ return false;
+
+ if (!(adapter->supported_settings & MGMT_SETTING_LE))
+ return false;
+
+ DBG("Setting static address");
+
+ if (!get_static_addr(adapter))
+ return false;
+
+ bacpy(&cp.bdaddr, &adapter->bdaddr);
+ if (mgmt_send(adapter->mgmt, MGMT_OP_SET_STATIC_ADDRESS,
+ adapter->dev_id, sizeof(cp), &cp,
+ NULL, NULL, NULL) > 0) {
+ return true;
+ }
+
+ return false;
+}
+
#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
static uint8_t *generate_irk(void)
{
}
#endif
-static void clear_devices_complete(uint8_t status, uint16_t length,
- const void *param, void *user_data)
-{
- if (status != MGMT_STATUS_SUCCESS) {
- error("Failed to clear devices: %s (0x%02x)",
- mgmt_errstr(status), status);
- return;
- }
-}
-
-static int clear_devices(struct btd_adapter *adapter)
-{
- struct mgmt_cp_remove_device cp;
-
- if (!kernel_conn_control)
- return 0;
-
- memset(&cp, 0, sizeof(cp));
-
- DBG("sending clear devices command for index %u", adapter->dev_id);
-
- if (mgmt_send(adapter->mgmt, MGMT_OP_REMOVE_DEVICE,
- adapter->dev_id, sizeof(cp), &cp,
- clear_devices_complete, adapter, NULL) > 0)
- return 0;
-
- btd_error(adapter->dev_id, "Failed to clear devices for index %u",
- adapter->dev_id);
-
- return -EIO;
-}
-
#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
static gboolean adapter_start_idle_cb(gpointer user_data)
{
goto failed;
}
- if (bacmp(&rp->bdaddr, BDADDR_ANY) == 0) {
- btd_error(adapter->dev_id, "No Bluetooth address for index %u",
- adapter->dev_id);
- goto failed;
- }
-
/*
- * Store controller information for device address, class of device,
- * device name, short name and settings.
+ * Store controller information for class of device, device
+ * name, short name and settings.
*
* During the lifetime of the controller these will be updated by
* events and the information is required to keep the current
* state of the controller.
*/
- bacpy(&adapter->bdaddr, &rp->bdaddr);
adapter->dev_class = rp->dev_class[0] | (rp->dev_class[1] << 8) |
(rp->dev_class[2] << 16);
adapter->name = g_strdup((const char *) rp->name);
adapter->short_name = g_strdup((const char *) rp->short_name);
+ adapter->manufacturer = btohs(rp->manufacturer);
+
adapter->supported_settings = btohl(rp->supported_settings);
adapter->current_settings = btohl(rp->current_settings);
clear_uuids(adapter);
clear_devices(adapter);
+ if (bacmp(&rp->bdaddr, BDADDR_ANY) == 0) {
+ if (!set_static_addr(adapter)) {
+ btd_error(adapter->dev_id,
+ "No Bluetooth address for index %u",
+ adapter->dev_id);
+ goto failed;
+ }
+ } else {
+ bacpy(&adapter->bdaddr, &rp->bdaddr);
+ if (!(adapter->supported_settings & MGMT_SETTING_LE))
+ adapter->bdaddr_type = BDADDR_BREDR;
+ else
+ adapter->bdaddr_type = BDADDR_LE_PUBLIC;
+ }
+
missing_settings = adapter->current_settings ^
adapter->supported_settings;
void btd_unregister_adapter_driver(struct btd_adapter_driver *driver);
guint btd_request_authorization(const bdaddr_t *src, const bdaddr_t *dst,
const char *uuid, service_auth_cb cb, void *user_data);
+guint btd_request_authorization_cable_configured(const bdaddr_t *src, const bdaddr_t *dst,
+ const char *uuid, service_auth_cb cb, void *user_data);
int btd_cancel_authorization(guint id);
int btd_adapter_restore_powered(struct btd_adapter *adapter);
#include <stdint.h>
#include <stdbool.h>
+#include <errno.h>
#include <dbus/dbus.h>
#include <gdbus/gdbus.h>
#include "dbus-common.h"
#include "error.h"
#include "log.h"
+#include "eir.h"
#include "src/shared/ad.h"
#include "src/shared/mgmt.h"
#include "src/shared/queue.h"
struct mgmt *mgmt;
uint16_t mgmt_index;
uint8_t max_adv_len;
+ uint8_t max_scan_rsp_len;
uint8_t max_ads;
+ uint32_t supported_flags;
unsigned int instance_bitmap;
};
struct btd_adv_manager *manager;
char *owner;
char *path;
+ char *name;
+ uint16_t appearance;
+ uint16_t duration;
+ uint16_t timeout;
+ unsigned int to_id;
GDBusClient *client;
GDBusProxy *proxy;
DBusMessage *reg;
uint8_t type; /* Advertising type */
- bool include_tx_power;
+ uint32_t flags;
struct bt_ad *data;
+ struct bt_ad *scan;
uint8_t instance;
};
{
struct btd_adv_client *client = data;
+ if (client->to_id > 0)
+ g_source_remove(client->to_id);
+
if (client->client) {
g_dbus_client_set_disconnect_watch(client->client, NULL, NULL);
g_dbus_client_unref(client->client);
client->instance);
bt_ad_unref(client->data);
+ bt_ad_unref(client->scan);
g_dbus_proxy_unref(client->proxy);
if (client->path)
g_free(client->path);
+ free(client->name);
free(client);
}
static void client_release(void *data)
{
struct btd_adv_client *client = data;
- DBusMessage *message;
DBG("Releasing advertisement %s, %s", client->owner, client->path);
- message = dbus_message_new_method_call(client->owner, client->path,
- LE_ADVERTISEMENT_IFACE,
- "Release");
-
- if (!message) {
- error("Couldn't allocate D-Bus message");
- return;
- }
-
- g_dbus_send_message(btd_get_dbus_connection(), message);
+ g_dbus_proxy_method_call(client->proxy, "Release", NULL, NULL, NULL,
+ NULL);
}
static void client_destroy(void *data)
client_free(data);
}
+static void remove_advertising(struct btd_adv_manager *manager,
+ uint8_t instance)
+{
+ struct mgmt_cp_remove_advertising cp;
+
+ if (instance)
+ DBG("instance %u", instance);
+ else
+ DBG("all instances");
+
+ cp.instance = instance;
+
+ mgmt_send(manager->mgmt, MGMT_OP_REMOVE_ADVERTISING,
+ manager->mgmt_index, sizeof(cp), &cp, NULL, NULL, NULL);
+}
+
static void client_remove(void *data)
{
struct btd_adv_client *client = data;
queue_remove(client->manager->clients, client);
g_idle_add(client_free_idle_cb, client);
+
+ g_dbus_emit_property_changed(btd_get_dbus_connection(),
+ adapter_get_path(client->manager->adapter),
+ LE_ADVERTISING_MGR_IFACE, "SupportedInstances");
+
+ g_dbus_emit_property_changed(btd_get_dbus_connection(),
+ adapter_get_path(client->manager->adapter),
+ LE_ADVERTISING_MGR_IFACE, "ActiveInstances");
}
static void client_disconnect_cb(DBusConnection *conn, void *user_data)
client_remove(user_data);
}
-static bool parse_type(GDBusProxy *proxy, uint8_t *type)
+static bool parse_type(DBusMessageIter *iter, struct btd_adv_client *client)
{
- DBusMessageIter iter;
const char *msg_type;
- if (!g_dbus_proxy_get_property(proxy, "Type", &iter))
+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING)
return false;
- if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
- return false;
-
- dbus_message_iter_get_basic(&iter, &msg_type);
+ dbus_message_iter_get_basic(iter, &msg_type);
if (!g_strcmp0(msg_type, "broadcast")) {
- *type = AD_TYPE_BROADCAST;
+ client->type = AD_TYPE_BROADCAST;
return true;
}
if (!g_strcmp0(msg_type, "peripheral")) {
- *type = AD_TYPE_PERIPHERAL;
+ client->type = AD_TYPE_PERIPHERAL;
return true;
}
return false;
}
-static bool parse_service_uuids(GDBusProxy *proxy, struct bt_ad *data)
+static bool parse_service_uuids(DBusMessageIter *iter,
+ struct btd_adv_client *client)
{
- DBusMessageIter iter, ariter;
-
- if (!g_dbus_proxy_get_property(proxy, "ServiceUUIDs", &iter))
- return true;
+ DBusMessageIter ariter;
- if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
return false;
- dbus_message_iter_recurse(&iter, &ariter);
+ dbus_message_iter_recurse(iter, &ariter);
- bt_ad_clear_service_uuid(data);
+ bt_ad_clear_service_uuid(client->data);
while (dbus_message_iter_get_arg_type(&ariter) == DBUS_TYPE_STRING) {
const char *uuid_str;
if (bt_string_to_uuid(&uuid, uuid_str) < 0)
goto fail;
- if (!bt_ad_add_service_uuid(data, &uuid))
+ if (!bt_ad_add_service_uuid(client->data, &uuid))
goto fail;
dbus_message_iter_next(&ariter);
return true;
fail:
- bt_ad_clear_service_uuid(data);
+ bt_ad_clear_service_uuid(client->data);
return false;
}
-static bool parse_solicit_uuids(GDBusProxy *proxy, struct bt_ad *data)
+static bool parse_solicit_uuids(DBusMessageIter *iter,
+ struct btd_adv_client *client)
{
- DBusMessageIter iter, ariter;
+ DBusMessageIter ariter;
- if (!g_dbus_proxy_get_property(proxy, "SolicitUUIDs", &iter))
- return true;
-
- if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
return false;
- dbus_message_iter_recurse(&iter, &ariter);
+ dbus_message_iter_recurse(iter, &ariter);
- bt_ad_clear_solicit_uuid(data);
+ bt_ad_clear_solicit_uuid(client->data);
while (dbus_message_iter_get_arg_type(&ariter) == DBUS_TYPE_STRING) {
const char *uuid_str;
if (bt_string_to_uuid(&uuid, uuid_str) < 0)
goto fail;
- if (!bt_ad_add_solicit_uuid(data, &uuid))
+ if (!bt_ad_add_solicit_uuid(client->data, &uuid))
goto fail;
dbus_message_iter_next(&ariter);
return true;
fail:
- bt_ad_clear_solicit_uuid(data);
+ bt_ad_clear_solicit_uuid(client->data);
return false;
}
-static bool parse_manufacturer_data(GDBusProxy *proxy, struct bt_ad *data)
+static bool parse_manufacturer_data(DBusMessageIter *iter,
+ struct btd_adv_client *client)
{
- DBusMessageIter iter, entries;
-
- if (!g_dbus_proxy_get_property(proxy, "ManufacturerData", &iter))
- return true;
+ DBusMessageIter entries;
- if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
return false;
- dbus_message_iter_recurse(&iter, &entries);
+ dbus_message_iter_recurse(iter, &entries);
- bt_ad_clear_manufacturer_data(data);
+ bt_ad_clear_manufacturer_data(client->data);
while (dbus_message_iter_get_arg_type(&entries)
== DBUS_TYPE_DICT_ENTRY) {
DBG("Adding ManufacturerData for %04x", manuf_id);
- if (!bt_ad_add_manufacturer_data(data, manuf_id, manuf_data,
- len))
+ if (!bt_ad_add_manufacturer_data(client->data, manuf_id,
+ manuf_data, len))
goto fail;
dbus_message_iter_next(&entries);
return true;
fail:
- bt_ad_clear_manufacturer_data(data);
+ bt_ad_clear_manufacturer_data(client->data);
return false;
}
-static bool parse_service_data(GDBusProxy *proxy, struct bt_ad *data)
+static bool parse_service_data(DBusMessageIter *iter,
+ struct btd_adv_client *client)
{
- DBusMessageIter iter, entries;
-
- if (!g_dbus_proxy_get_property(proxy, "ServiceData", &iter))
- return true;
+ DBusMessageIter entries;
- if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
return false;
- dbus_message_iter_recurse(&iter, &entries);
+ dbus_message_iter_recurse(iter, &entries);
- bt_ad_clear_service_data(data);
+ bt_ad_clear_service_data(client->data);
while (dbus_message_iter_get_arg_type(&entries)
== DBUS_TYPE_DICT_ENTRY) {
DBG("Adding ServiceData for %s", uuid_str);
- if (!bt_ad_add_service_data(data, &uuid, service_data, len))
+ if (!bt_ad_add_service_data(client->data, &uuid, service_data,
+ len))
goto fail;
dbus_message_iter_next(&entries);
return true;
fail:
- bt_ad_clear_service_data(data);
+ bt_ad_clear_service_data(client->data);
return false;
}
-static bool parse_include_tx_power(GDBusProxy *proxy, bool *included)
+static struct adv_include {
+ uint8_t flag;
+ const char *name;
+} includes[] = {
+ { MGMT_ADV_FLAG_TX_POWER, "tx-power" },
+ { MGMT_ADV_FLAG_APPEARANCE, "appearance" },
+ { MGMT_ADV_FLAG_LOCAL_NAME, "local-name" },
+ { },
+};
+
+static bool parse_includes(DBusMessageIter *iter,
+ struct btd_adv_client *client)
+{
+ DBusMessageIter entries;
+
+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
+ return false;
+
+ dbus_message_iter_recurse(iter, &entries);
+
+ while (dbus_message_iter_get_arg_type(&entries) == DBUS_TYPE_STRING) {
+ const char *str;
+ struct adv_include *inc;
+
+ dbus_message_iter_get_basic(&entries, &str);
+
+ for (inc = includes; inc && inc->name; inc++) {
+ if (strcmp(str, inc->name))
+ continue;
+
+ if (!(client->manager->supported_flags & inc->flag))
+ continue;
+
+ DBG("Including Feature: %s", str);
+
+ client->flags |= inc->flag;
+ }
+
+ dbus_message_iter_next(&entries);
+ }
+
+ return true;
+}
+
+static bool parse_local_name(DBusMessageIter *iter,
+ struct btd_adv_client *client)
{
- DBusMessageIter iter;
- dbus_bool_t b;
+ const char *name;
- if (!g_dbus_proxy_get_property(proxy, "IncludeTxPower", &iter))
- return true;
+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING)
+ return false;
- if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_BOOLEAN)
+ if (client->flags & MGMT_ADV_FLAG_LOCAL_NAME) {
+ error("Local name already included");
return false;
+ }
- dbus_message_iter_get_basic(&iter, &b);
+ dbus_message_iter_get_basic(iter, &name);
- *included = b;
+ free(client->name);
+ client->name = strdup(name);
return true;
}
-static void add_client_complete(struct btd_adv_client *client, uint8_t status)
+static bool parse_appearance(DBusMessageIter *iter,
+ struct btd_adv_client *client)
{
- DBusMessage *reply;
+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT16)
+ return false;
- if (status) {
- error("Failed to add advertisement: %s (0x%02x)",
- mgmt_errstr(status), status);
- reply = btd_error_failed(client->reg,
- "Failed to register advertisement");
- queue_remove(client->manager->clients, client);
- g_idle_add(client_free_idle_cb, client);
+ if (client->flags & MGMT_ADV_FLAG_APPEARANCE) {
+ error("Appearance already included");
+ return false;
+ }
- } else
- reply = dbus_message_new_method_return(client->reg);
+ dbus_message_iter_get_basic(iter, &client->appearance);
- g_dbus_send_message(btd_get_dbus_connection(), reply);
- dbus_message_unref(client->reg);
- client->reg = NULL;
+ return true;
}
-static void add_adv_callback(uint8_t status, uint16_t length,
- const void *param, void *user_data)
+static bool parse_duration(DBusMessageIter *iter,
+ struct btd_adv_client *client)
+{
+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT16)
+ return false;
+
+ dbus_message_iter_get_basic(iter, &client->duration);
+
+ return true;
+}
+
+static gboolean client_timeout(void *user_data)
{
struct btd_adv_client *client = user_data;
- const struct mgmt_rp_add_advertising *rp = param;
- if (status)
- goto done;
+ DBG("");
- if (!param || length < sizeof(*rp)) {
- status = MGMT_STATUS_FAILED;
- goto done;
- }
+ client->to_id = 0;
- client->instance = rp->instance;
+ client_release(client);
+ client_remove(client);
- g_dbus_client_set_disconnect_watch(client->client, client_disconnect_cb,
- client);
- DBG("Advertisement registered: %s", client->path);
+ return FALSE;
+}
-done:
- add_client_complete(client, status);
+static bool parse_timeout(DBusMessageIter *iter,
+ struct btd_adv_client *client)
+{
+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT16)
+ return false;
+
+ dbus_message_iter_get_basic(iter, &client->timeout);
+
+ if (client->to_id)
+ g_source_remove(client->to_id);
+
+ client->to_id = g_timeout_add_seconds(client->timeout, client_timeout,
+ client);
+
+ return true;
}
+static struct adv_parser {
+ const char *name;
+ bool (*func)(DBusMessageIter *iter, struct btd_adv_client *client);
+} parsers[] = {
+ { "Type", parse_type },
+ { "ServiceUUIDs", parse_service_uuids },
+ { "SolicitUUIDs", parse_solicit_uuids },
+ { "ManufacturerData", parse_manufacturer_data },
+ { "ServiceData", parse_service_data },
+ { "Includes", parse_includes },
+ { "LocalName", parse_local_name },
+ { "Appearance", parse_appearance },
+ { "Duration", parse_duration },
+ { "Timeout", parse_timeout },
+ { },
+};
+
static size_t calc_max_adv_len(struct btd_adv_client *client, uint32_t flags)
{
size_t max = client->manager->max_adv_len;
return max;
}
-static DBusMessage *refresh_advertisement(struct btd_adv_client *client)
+static uint8_t *generate_adv_data(struct btd_adv_client *client,
+ uint32_t *flags, size_t *len)
+{
+ if ((*flags & MGMT_ADV_FLAG_APPEARANCE) ||
+ client->appearance != UINT16_MAX) {
+ uint16_t appearance;
+
+ appearance = client->appearance;
+ if (appearance == UINT16_MAX)
+ /* TODO: Get the appearance from the adaptor once
+ * supported.
+ */
+ appearance = 0x000;
+
+ bt_ad_add_appearance(client->data, appearance);
+ }
+
+ return bt_ad_generate(client->data, len);
+}
+
+static uint8_t *generate_scan_rsp(struct btd_adv_client *client,
+ uint32_t *flags, size_t *len)
+{
+ struct btd_adv_manager *manager = client->manager;
+ const char *name;
+
+ if (!(*flags & MGMT_ADV_FLAG_LOCAL_NAME) && !client->name) {
+ *len = 0;
+ return NULL;
+ }
+
+ *flags &= ~MGMT_ADV_FLAG_LOCAL_NAME;
+
+ name = client->name;
+ if (!name)
+ name = btd_adapter_get_name(manager->adapter);
+
+ bt_ad_add_name(client->scan, name);
+
+ return bt_ad_generate(client->scan, len);
+}
+
+static int refresh_adv(struct btd_adv_client *client, mgmt_request_func_t func)
{
struct mgmt_cp_add_advertising *cp;
uint8_t param_len;
uint8_t *adv_data;
size_t adv_data_len;
+ uint8_t *scan_rsp;
+ size_t scan_rsp_len = -1;
uint32_t flags = 0;
DBG("Refreshing advertisement: %s", client->path);
if (client->type == AD_TYPE_PERIPHERAL)
flags = MGMT_ADV_FLAG_CONNECTABLE | MGMT_ADV_FLAG_DISCOV;
- if (client->include_tx_power)
- flags |= MGMT_ADV_FLAG_TX_POWER;
-
- adv_data = bt_ad_generate(client->data, &adv_data_len);
+ flags |= client->flags;
+ adv_data = generate_adv_data(client, &flags, &adv_data_len);
if (!adv_data || (adv_data_len > calc_max_adv_len(client, flags))) {
error("Advertising data too long or couldn't be generated.");
+ return -EINVAL;
+ }
- return g_dbus_create_error(client->reg, ERROR_INTERFACE
- ".InvalidLength",
- "Advertising data too long.");
+ scan_rsp = generate_scan_rsp(client, &flags, &scan_rsp_len);
+ if (!scan_rsp && scan_rsp_len) {
+ error("Scan data couldn't be generated.");
+ return -EINVAL;
}
- param_len = sizeof(struct mgmt_cp_add_advertising) + adv_data_len;
+ param_len = sizeof(struct mgmt_cp_add_advertising) + adv_data_len +
+ scan_rsp_len;
cp = malloc0(param_len);
-
if (!cp) {
error("Couldn't allocate for MGMT!");
-
free(adv_data);
-
- return btd_error_failed(client->reg, "Failed");
+ free(scan_rsp);
+ return -ENOMEM;
}
cp->flags = htobl(flags);
cp->instance = client->instance;
+ cp->duration = client->duration;
cp->adv_data_len = adv_data_len;
+ cp->scan_rsp_len = scan_rsp_len;
memcpy(cp->data, adv_data, adv_data_len);
+ memcpy(cp->data + adv_data_len, scan_rsp, scan_rsp_len);
free(adv_data);
+ free(scan_rsp);
if (!mgmt_send(client->manager->mgmt, MGMT_OP_ADD_ADVERTISING,
client->manager->mgmt_index, param_len, cp,
- add_adv_callback, client, NULL)) {
+ func, client, NULL)) {
error("Failed to add Advertising Data");
-
free(cp);
-
- return btd_error_failed(client->reg, "Failed");
+ return -EINVAL;
}
free(cp);
- return NULL;
+ return 0;
}
-static DBusMessage *parse_advertisement(struct btd_adv_client *client)
+static void properties_changed(GDBusProxy *proxy, const char *name,
+ DBusMessageIter *iter, void *user_data)
{
- if (!parse_type(client->proxy, &client->type)) {
- error("Failed to read \"Type\" property of advertisement");
- goto fail;
- }
+ struct btd_adv_client *client = user_data;
+ struct adv_parser *parser;
- if (!parse_service_uuids(client->proxy, client->data)) {
- error("Property \"ServiceUUIDs\" failed to parse");
- goto fail;
- }
+ for (parser = parsers; parser && parser->name; parser++) {
+ if (strcmp(parser->name, name))
+ continue;
- if (!parse_solicit_uuids(client->proxy, client->data)) {
- error("Property \"SolicitUUIDs\" failed to parse");
- goto fail;
+ if (parser->func(iter, client)) {
+ refresh_adv(client, NULL);
+ break;
+ }
}
+}
- if (!parse_manufacturer_data(client->proxy, client->data)) {
- error("Property \"ManufacturerData\" failed to parse");
- goto fail;
- }
+static void add_client_complete(struct btd_adv_client *client, uint8_t status)
+{
+ DBusMessage *reply;
- if (!parse_service_data(client->proxy, client->data)) {
- error("Property \"ServiceData\" failed to parse");
- goto fail;
+ if (status) {
+ error("Failed to add advertisement: %s (0x%02x)",
+ mgmt_errstr(status), status);
+ reply = btd_error_failed(client->reg,
+ "Failed to register advertisement");
+ queue_remove(client->manager->clients, client);
+ g_idle_add(client_free_idle_cb, client);
+
+ } else
+ reply = dbus_message_new_method_return(client->reg);
+
+ g_dbus_send_message(btd_get_dbus_connection(), reply);
+ dbus_message_unref(client->reg);
+ client->reg = NULL;
+}
+
+static void add_adv_callback(uint8_t status, uint16_t length,
+ const void *param, void *user_data)
+{
+ struct btd_adv_client *client = user_data;
+ const struct mgmt_rp_add_advertising *rp = param;
+
+ if (status)
+ goto done;
+
+ if (!param || length < sizeof(*rp)) {
+ status = MGMT_STATUS_FAILED;
+ goto done;
}
- if (!parse_include_tx_power(client->proxy, &client->include_tx_power)) {
- error("Property \"IncludeTxPower\" failed to parse");
- goto fail;
+ client->instance = rp->instance;
+
+ g_dbus_client_set_disconnect_watch(client->client, client_disconnect_cb,
+ client);
+ DBG("Advertisement registered: %s", client->path);
+
+ g_dbus_emit_property_changed(btd_get_dbus_connection(),
+ adapter_get_path(client->manager->adapter),
+ LE_ADVERTISING_MGR_IFACE, "SupportedInstances");
+
+ g_dbus_emit_property_changed(btd_get_dbus_connection(),
+ adapter_get_path(client->manager->adapter),
+ LE_ADVERTISING_MGR_IFACE, "ActiveInstances");
+
+ g_dbus_proxy_set_property_watch(client->proxy, properties_changed,
+ client);
+
+done:
+ add_client_complete(client, status);
+}
+
+static DBusMessage *parse_advertisement(struct btd_adv_client *client)
+{
+ struct adv_parser *parser;
+ int err;
+
+ for (parser = parsers; parser && parser->name; parser++) {
+ DBusMessageIter iter;
+
+ if (!g_dbus_proxy_get_property(client->proxy, parser->name,
+ &iter))
+ continue;
+
+ if (!parser->func(&iter, client)) {
+ error("Error parsing %s property", parser->name);
+ goto fail;
+ }
}
- return refresh_advertisement(client);
+ err = refresh_adv(client, add_adv_callback);
+ if (!err)
+ return NULL;
fail:
return btd_error_failed(client->reg, "Failed to parse advertisement.");
if (!client->data)
goto fail;
- client->instance = util_get_uid(&manager->instance_bitmap,
- manager->max_ads);
- if (!client->instance)
+ client->scan = bt_ad_new();
+ if (!client->scan)
goto fail;
client->manager = manager;
+ client->appearance = UINT16_MAX;
return client;
return btd_error_failed(msg,
"Failed to register advertisement");
+ client->instance = util_get_uid(&manager->instance_bitmap,
+ manager->max_ads);
+ if (!client->instance) {
+ client_free(client);
+ return btd_error_not_permitted(msg,
+ "Maximum advertisements reached");
+ }
+
DBG("Registered advertisement at path %s", match.path);
queue_push_tail(manager->clients, client);
return dbus_message_new_method_return(msg);
}
+static gboolean get_instances(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct btd_adv_manager *manager = data;
+ uint8_t instances;
+
+ instances = manager->max_ads - queue_length(manager->clients);
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_BYTE, &instances);
+
+ return TRUE;
+}
+
+static gboolean get_active_instances(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct btd_adv_manager *manager = data;
+ uint8_t instances;
+
+ instances = queue_length(manager->clients);
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_BYTE, &instances);
+
+ return TRUE;
+}
+
+static void append_include(struct btd_adv_manager *manager,
+ DBusMessageIter *iter)
+{
+ struct adv_include *inc;
+
+ for (inc = includes; inc && inc->name; inc++) {
+ if (manager->supported_flags & inc->flag)
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
+ &inc->name);
+ }
+}
+
+static gboolean get_supported_includes(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct btd_adv_manager *manager = data;
+ DBusMessageIter entry;
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+ DBUS_TYPE_STRING_AS_STRING, &entry);
+
+ append_include(manager, &entry);
+
+ dbus_message_iter_close_container(iter, &entry);
+
+ return TRUE;
+}
+
+static const GDBusPropertyTable properties[] = {
+ { "ActiveInstances", "y", get_active_instances, NULL, NULL },
+ { "SupportedInstances", "y", get_instances, NULL, NULL },
+ { "SupportedIncludes", "as", get_supported_includes, NULL, NULL },
+ { }
+};
+
static const GDBusMethodTable methods[] = {
- { GDBUS_EXPERIMENTAL_ASYNC_METHOD("RegisterAdvertisement",
+ { GDBUS_ASYNC_METHOD("RegisterAdvertisement",
GDBUS_ARGS({ "advertisement", "o" },
{ "options", "a{sv}" }),
NULL, register_advertisement) },
- { GDBUS_EXPERIMENTAL_ASYNC_METHOD("UnregisterAdvertisement",
+ { GDBUS_ASYNC_METHOD("UnregisterAdvertisement",
GDBUS_ARGS({ "service", "o" }),
NULL,
unregister_advertisement) },
}
manager->max_adv_len = feat->max_adv_data_len;
+ manager->max_scan_rsp_len = feat->max_scan_rsp_len;
manager->max_ads = feat->max_instances;
+ manager->supported_flags |= feat->supported_flags;
if (manager->max_ads == 0)
return;
if (!g_dbus_register_interface(btd_get_dbus_connection(),
adapter_get_path(manager->adapter),
- LE_ADVERTISING_MGR_IFACE,
- methods, NULL, NULL, manager, NULL))
+ LE_ADVERTISING_MGR_IFACE, methods,
+ NULL, properties, manager, NULL)) {
error("Failed to register " LE_ADVERTISING_MGR_IFACE);
+ return;
+ }
+
+ /* Reset existing instances */
+ if (feat->num_instances)
+ remove_advertising(manager, 0);
}
static struct btd_adv_manager *manager_create(struct btd_adapter *adapter)
}
manager->clients = queue_new();
+ manager->supported_flags = MGMT_ADV_FLAG_LOCAL_NAME;
return manager;
}
<allow send_interface="org.bluez.CyclingSpeedWatcher1"/>
<allow send_interface="org.bluez.GattCharacteristic1"/>
<allow send_interface="org.bluez.GattDescriptor1"/>
+ <allow send_interface="org.bluez.LEAdvertisement1"/>
<allow send_interface="org.freedesktop.DBus.ObjectManager"/>
<allow send_interface="org.freedesktop.DBus.Properties"/>
</policy>
Enable logging in foreground. Directs log output to the controlling terminal \
in addition to syslog.
.TP
+.B -f, --configfile
+Specifies an explicit config file path instead of relying on the default path \
+(@CONFIGDIR@/main.conf) for the config file.
+.TP
.B -d, --debug=<file1>:<file2>:...
Sets how much information bluetoothd sends to the log destination (usually \
syslog's "daemon" facility). If the file options are omitted, then debugging \
gboolean secure;
};
+enum {
+ BROWSE_SDP,
+ BROWSE_GATT
+};
+
struct browse_req {
DBusMessage *msg;
struct btd_device *device;
+ uint8_t type;
GSList *match_uuids;
GSList *profiles_added;
sdp_list_t *records;
struct btd_device {
int ref_count;
+ bdaddr_t conn_bdaddr;
+ uint8_t conn_bdaddr_type;
bdaddr_t bdaddr;
uint8_t bdaddr_type;
char *path;
GSList *pending; /* Pending services */
GSList *watches; /* List of disconnect_data */
bool temporary;
+ bool connectable;
guint disconn_timer;
guint discov_timer;
struct browse_req *browse; /* service discover request */
* attribute cache support can be built.
*/
struct gatt_db *db; /* GATT db cache */
+ unsigned int db_id;
struct bt_gatt_client *client; /* GATT client instance */
struct bt_gatt_server *server; /* GATT server instance */
+ unsigned int gatt_ready_id;
struct btd_gatt_client *client_dbus;
#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
DBG("");
#endif
+ struct btd_device *device = req->device;
+
+ if (device->browse == req)
+ device->browse = NULL;
+
if (req->listener_id)
g_dbus_remove_watch(dbus_conn, req->listener_id);
if (req->msg)
g_free(req);
}
+static bool gatt_cache_is_enabled(struct btd_device *device)
+{
+ switch (main_opts.gatt_cache) {
+ case BT_GATT_CACHE_YES:
+ return device_is_paired(device, device->bdaddr_type);
+ case BT_GATT_CACHE_NO:
+ return false;
+ case BT_GATT_CACHE_ALWAYS:
+ default:
+ return true;
+ }
+}
+
+static void gatt_cache_cleanup(struct btd_device *device)
+{
+ if (gatt_cache_is_enabled(device))
+ return;
+
+ gatt_db_clear(device->db);
+}
+
static void gatt_client_cleanup(struct btd_device *device)
{
if (!device->client)
return;
+ gatt_cache_cleanup(device);
bt_gatt_client_set_service_changed(device->client, NULL, NULL, NULL);
- bt_gatt_client_set_ready_handler(device->client, NULL, NULL, NULL);
+
+ if (device->gatt_ready_id > 0) {
+ bt_gatt_client_ready_unregister(device->client,
+ device->gatt_ready_id);
+ device->gatt_ready_id = 0;
+ }
+
bt_gatt_client_unref(device->client);
device->client = NULL;
}
attio_cleanup(device);
- device->browse = NULL;
browse_request_free(req);
}
g_slist_free_full(device->svc_callbacks, svc_dev_remove);
/* Reset callbacks since the device is going to be freed */
- gatt_db_register(device->db, NULL, NULL, NULL, NULL);
+ gatt_db_unregister(device->db, device->db_id);
attio_cleanup(device);
ba2str(device->rpa, dstaddr);
else
#endif
- ba2str(&device->bdaddr, dstaddr);
+ ba2str(&device->bdaddr, dstaddr);
dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &ptr);
return TRUE;
}
+static gboolean property_get_address_type(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *user_data)
+{
+ struct btd_device *device = user_data;
+ const char *str;
+
+ if (device->le && device->bdaddr_type == BDADDR_LE_RANDOM)
+ str = "random";
+ else
+ str = "public";
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &str);
+
+ return TRUE;
+}
+
static gboolean dev_property_get_name(const GDBusPropertyTable *property,
DBusMessageIter *iter, void *data)
{
struct btd_device *device = user_data;
/*
- * Disable connections through passive scanning until
- * Device1.Connect is called
+ * If device is not trusted disable connections through passive
+ * scanning until Device1.Connect is called
*/
- if (device->auto_connect) {
+ if (device->auto_connect && !device->trusted) {
device->disable_auto_connect = TRUE;
device_set_auto_connect(device, FALSE);
}
l = find_service_with_state(dev->services, BTD_SERVICE_STATE_CONNECTED);
- if (err && l == NULL)
+ if (err && l == NULL) {
+ /* Fallback to LE bearer if supported */
+ if (err == -EHOSTDOWN && dev->le && !dev->le_state.connected) {
+ err = device_connect_le(dev);
+ if (err == 0)
+ return;
+ }
+
g_dbus_send_message(dbus_conn,
btd_error_failed(dev->connect, strerror(-err)));
- else {
+ } else {
#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
/* SDP is not required for Samsung TV Power on */
if (g_strcmp0(profile->name, "hid-device") == 0) {
DEVICE_INTERFACE, "ManufacturerData");
}
-void device_set_manufacturer_data(struct btd_device *dev, GSList *list)
+void device_set_manufacturer_data(struct btd_device *dev, GSList *list,
+ bool duplicate)
{
+ if (duplicate)
+ bt_ad_clear_manufacturer_data(dev->ad);
+
g_slist_foreach(list, add_manufacturer_data, dev);
}
DEVICE_INTERFACE, "ServiceData");
}
-void device_set_service_data(struct btd_device *dev, GSList *list)
+void device_set_service_data(struct btd_device *dev, GSList *list,
+ bool duplicate)
{
+ if (duplicate)
+ bt_ad_clear_service_data(dev->ad);
+
g_slist_foreach(list, add_service_data, dev);
}
return;
}
+ if (!gatt_cache_is_enabled(device))
+ return;
+
ba2str(btd_adapter_get_address(adapter), src_addr);
ba2str(&device->bdaddr, dst_addr);
}
-static void browse_request_complete(struct browse_req *req, uint8_t bdaddr_type,
- int err)
+static void browse_request_complete(struct browse_req *req, uint8_t type,
+ uint8_t bdaddr_type, int err)
{
struct btd_device *dev = req->device;
DBusMessage *reply = NULL;
+ if (req->type != type)
+ return;
+
if (!req->msg)
goto done;
}
if (err) {
+ /* Fallback to LE bearer if supported */
+ if (err == -EHOSTDOWN && bdaddr_type == BDADDR_BREDR &&
+ dev->le && !dev->le_state.connected) {
+ err = device_connect_le(dev);
+ if (err == 0)
+ goto done;
+ }
reply = btd_error_failed(req->msg, strerror(-err));
goto done;
}
DEVICE_INTERFACE, "ServicesResolved");
}
-static void device_svc_resolved(struct btd_device *dev, uint8_t bdaddr_type,
- int err)
+static void device_svc_resolved(struct btd_device *dev, uint8_t browse_type,
+ uint8_t bdaddr_type, int err)
{
struct bearer_state *state = get_state(dev, bdaddr_type);
struct browse_req *req = dev->browse;
return;
}
#endif
-
- dev->browse = NULL;
- browse_request_complete(req, bdaddr_type, err);
+ browse_request_complete(req, browse_type, bdaddr_type, err);
}
static struct bonding_req *bonding_request_new(DBusMessage *msg,
static const GDBusPropertyTable device_properties[] = {
{ "Address", "s", dev_property_get_address },
+ { "AddressType", "s", property_get_address_type },
{ "Name", "s", dev_property_get_name, NULL, dev_property_exists_name },
{ "Alias", "s", dev_property_get_alias, dev_property_set_alias },
{ "Class", "u", dev_property_get_class, NULL,
return;
}
+ bacpy(&dev->conn_bdaddr, &dev->bdaddr);
+ dev->conn_bdaddr_type = dev->bdaddr_type;
+
/* If this is the first connection over this bearer */
if (bdaddr_type == BDADDR_BREDR)
device_set_bredr_support(dev);
char **keys, filename[PATH_MAX];
GKeyFile *key_file;
+ if (!gatt_cache_is_enabled(device))
+ return;
+
DBG("Restoring %s gatt database from file", peer);
snprintf(filename, PATH_MAX, STORAGEDIR "/%s/cache/%s", local, peer);
device->adapter = adapter;
device->temporary = true;
- gatt_db_register(device->db, gatt_service_added, gatt_service_removed,
- device, NULL);
+ device->db_id = gatt_db_register(device->db, gatt_service_added,
+ gatt_service_removed, device, NULL);
return btd_device_ref(device);
}
g_dbus_emit_property_changed(dbus_conn, device->path,
DEVICE_INTERFACE, "Address");
+ g_dbus_emit_property_changed(dbus_conn, device->path,
+ DEVICE_INTERFACE, "AddressType");
}
void device_set_bredr_support(struct btd_device *device)
device->pending = NULL;
if (btd_device_is_connected(device)) {
- g_source_remove(device->disconn_timer);
+ if (device->disconn_timer > 0)
+ g_source_remove(device->disconn_timer);
disconnect_all(device);
}
if (!dev->le)
return -1;
- if (addr->bdaddr_type != dev->bdaddr_type)
+ if (addr->bdaddr_type != dev->bdaddr_type) {
+ if (addr->bdaddr_type == dev->conn_bdaddr_type)
+ return bacmp(&dev->conn_bdaddr, &addr->bdaddr);
return -1;
+ }
return cmp;
}
done:
#endif
- device_svc_resolved(device, BDADDR_BREDR, err);
+ device_svc_resolved(device, BROWSE_SDP, BDADDR_BREDR, err);
}
static void browse_cb(sdp_list_t *recs, int err, gpointer user_data)
DBG("status: %s, error: %u", success ? "success" : "failed", att_ecode);
if (!success) {
- device_svc_resolved(device, device->bdaddr_type, -EIO);
+ device_svc_resolved(device, BROWSE_GATT, device->bdaddr_type,
+ -EIO);
return;
}
register_gatt_services(device);
#endif
- device_svc_resolved(device, device->bdaddr_type, 0);
+ device_svc_resolved(device, BROWSE_GATT, device->bdaddr_type, 0);
store_gatt_db(device);
*/
device_accept_gatt_profiles(device);
- if (!bt_gatt_client_set_ready_handler(device->client,
+ device->gatt_ready_id = bt_gatt_client_ready_register(device->client,
gatt_client_ready_cb,
- device, NULL)) {
- DBG("Failed to set ready handler");
+ device, NULL);
+ if (!device->gatt_ready_id) {
+ DBG("Failed to register GATT ready callback");
gatt_client_cleanup(device);
return;
}
adapter_connect_list_add(device->adapter, device);
}
- if (device->browse) {
+ if (device->browse)
browse_request_complete(device->browse,
+ BROWSE_GATT,
device->bdaddr_type,
-ECONNABORTED);
- device->browse = NULL;
- }
err = -ECONNABORTED;
goto done;
}
static struct browse_req *browse_request_new(struct btd_device *device,
+ uint8_t type,
DBusMessage *msg)
{
struct browse_req *req;
req = g_new0(struct browse_req, 1);
req->device = device;
+ req->type = type;
device->browse = req;
#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
DBG("");
#endif
- req = browse_request_new(device, msg);
+ req = browse_request_new(device, BROWSE_GATT, msg);
if (!req)
return -EBUSY;
* Services have already been discovered, so signal this browse
* request as resolved.
*/
- device_svc_resolved(device, device->bdaddr_type, 0);
+ device_svc_resolved(device, BROWSE_GATT, device->bdaddr_type,
+ 0);
return 0;
}
BT_IO_OPT_INVALID);
if (device->att_io == NULL) {
- device->browse = NULL;
browse_request_free(req);
return -EIO;
}
#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
DBG("");
#endif
- req = browse_request_new(device, msg);
+ req = browse_request_new(device, BROWSE_SDP, msg);
if (!req)
return -EBUSY;
DEVICE_INTERFACE, "AdvertisingFlags");
}
+bool device_is_connectable(struct btd_device *device)
+{
+ if (!device)
+ return false;
+
+ if (device->bredr)
+ return true;
+
+ /* Check if either Limited or General discoverable are set */
+ return (device->ad_flags[0] & 0x03);
+}
+
static gboolean start_discovery(gpointer user_data)
{
struct btd_device *device = user_data;
state->paired = true;
- /* If the other bearer state was alraedy true we don't need to
+ /* If the other bearer state was already true we don't need to
* send any property signals.
*/
if (dev->bredr_state.paired == dev->le_state.paired)
return recs;
}
+void btd_device_set_record(struct btd_device *device, const char *uuid,
+ const char *record)
+{
+ /* This API is only used for BR/EDR */
+ struct bearer_state *state = &device->bredr_state;
+ struct browse_req *req;
+ sdp_list_t *recs = NULL;
+ sdp_record_t *rec;
+
+ if (!record)
+ return;
+
+ req = browse_request_new(device, BROWSE_SDP, NULL);
+ if (!req)
+ return;
+
+ rec = record_from_string(record);
+ recs = sdp_list_append(recs, rec);
+ update_bredr_services(req, recs);
+ sdp_list_free(recs, NULL);
+
+ device->svc_refreshed = true;
+ state->svc_resolved = true;
+
+ device_probe_profiles(device, req->profiles_added);
+
+ /* Propagate services changes */
+ g_dbus_emit_property_changed(dbus_conn, req->device->path,
+ DEVICE_INTERFACE, "UUIDs");
+
+ device_svc_resolved(device, BROWSE_SDP, device->bdaddr_type, 0);
+}
+
const sdp_record_t *btd_device_get_record(struct btd_device *device,
const char *uuid)
{
int device_addr_type_cmp(gconstpointer a, gconstpointer b);
GSList *btd_device_get_uuids(struct btd_device *device);
void device_probe_profiles(struct btd_device *device, GSList *profiles);
+
+void btd_device_set_record(struct btd_device *device, const char *uuid,
+ const char *record);
const sdp_record_t *btd_device_get_record(struct btd_device *device,
const char *uuid);
struct gatt_primary *btd_device_get_primary(struct btd_device *device,
bool device_attach_att(struct btd_device *dev, GIOChannel *io);
void btd_device_add_uuid(struct btd_device *device, const char *uuid);
void device_add_eir_uuids(struct btd_device *dev, GSList *uuids);
-void device_set_manufacturer_data(struct btd_device *dev, GSList *list);
-void device_set_service_data(struct btd_device *dev, GSList *list);
+void device_set_manufacturer_data(struct btd_device *dev, GSList *list,
+ bool duplicate);
+void device_set_service_data(struct btd_device *dev, GSList *list,
+ bool duplicate);
void device_probe_profile(gpointer a, gpointer b);
void device_remove_profile(gpointer a, gpointer b);
struct btd_adapter *device_get_adapter(struct btd_device *device);
uint32_t hfp_hs, uint32_t a2dp);
#endif
gboolean device_is_temporary(struct btd_device *device);
+bool device_is_connectable(struct btd_device *device);
bool device_is_paired(struct btd_device *device, uint8_t bdaddr_type);
bool device_is_bonded(struct btd_device *device, uint8_t bdaddr_type);
gboolean device_is_trusted(struct btd_device *device);
#include <stdbool.h>
#include <stdint.h>
#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
#include <dbus/dbus.h>
#include "error.h"
#include "adapter.h"
#include "device.h"
+#include "src/shared/io.h"
#include "src/shared/queue.h"
#include "src/shared/att.h"
#include "src/shared/gatt-db.h"
struct queue *services;
struct queue *all_notify_clients;
+ struct queue *ios;
#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
guint wait_charcs_id;
#endif
bt_uuid_t uuid;
char *path;
struct queue *chrcs;
+ struct queue *incl_services;
#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
bool chrcs_ready;
struct queue *pending_ext_props;
async_dbus_op_complete_t complete;
};
+struct pipe_io {
+ DBusMessage *msg;
+ struct io *io;
+ void (*destroy)(void *data);
+ void *data;
+};
+
struct characteristic {
struct service *service;
struct gatt_db_attribute *attr;
bt_uuid_t uuid;
char *path;
+ unsigned int ready_id;
+ struct pipe_io *write_io;
+ struct pipe_io *notify_io;
+
struct async_dbus_op *read_op;
struct async_dbus_op *write_op;
if (err) {
reply = err > 0 ? create_gatt_dbus_error(msg, err) :
btd_error_failed(msg, strerror(-err));
-
goto send_reply;
}
if (parse_value_arg(&iter, &value, &value_len))
return btd_error_invalid_args(msg);
+ dbus_message_iter_next(&iter);
+
if (parse_options(&iter, &offset))
return btd_error_invalid_args(msg);
return TRUE;
}
+static gboolean
+characteristic_get_write_acquired(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct characteristic *chrc = data;
+ dbus_bool_t locked = chrc->write_io ? TRUE : FALSE;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &locked);
+
+ return TRUE;
+}
+
+static gboolean
+characteristic_write_acquired_exists(const GDBusPropertyTable *property,
+ void *data)
+{
+ struct characteristic *chrc = data;
+
+ return (chrc->props & BT_GATT_CHRC_PROP_WRITE_WITHOUT_RESP);
+}
+
+static gboolean
+characteristic_get_notify_acquired(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct characteristic *chrc = data;
+ dbus_bool_t locked = chrc->notify_io ? TRUE : FALSE;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &locked);
+
+ return TRUE;
+}
+
+static gboolean
+characteristic_notify_acquired_exists(const GDBusPropertyTable *property,
+ void *data)
+{
+ struct characteristic *chrc = data;
+
+ return (chrc->props & BT_GATT_CHRC_PROP_NOTIFY);
+}
+
static void write_characteristic_cb(struct gatt_db_attribute *attr, int err,
void *user_data)
{
if (!gatt)
return btd_error_failed(msg, "Not connected");
+ if (chrc->write_io)
+ return btd_error_not_permitted(msg, "Write acquired");
+
if (chrc->write_op)
return btd_error_in_progress(msg);
if (parse_value_arg(&iter, &value, &value_len))
return btd_error_invalid_args(msg);
+ dbus_message_iter_next(&iter);
+
if (parse_options(&iter, &offset))
return btd_error_invalid_args(msg);
return btd_error_not_supported(msg);
}
+static bool chrc_pipe_read(struct io *io, void *user_data)
+{
+ struct characteristic *chrc = user_data;
+ struct bt_gatt_client *gatt = chrc->service->client->gatt;
+ uint8_t buf[512];
+ int fd = io_get_fd(io);
+ ssize_t bytes_read;
+
+ bytes_read = read(fd, buf, sizeof(buf));
+ if (bytes_read < 0)
+ return false;
+
+ if (!gatt)
+ return false;
+
+ bt_gatt_client_write_without_response(gatt, chrc->value_handle,
+ chrc->props & BT_GATT_CHRC_PROP_AUTH,
+ buf, bytes_read);
+
+ return true;
+}
+
+static void pipe_io_destroy(struct pipe_io *io)
+{
+ if (io->destroy)
+ io->destroy(io->data);
+
+ if (io->msg)
+ dbus_message_unref(io->msg);
+
+ io_destroy(io->io);
+ free(io);
+}
+
+static void characteristic_destroy_pipe(struct characteristic *chrc,
+ struct io *io)
+{
+ queue_remove(chrc->service->client->ios, io);
+
+ if (chrc->write_io && io == chrc->write_io->io) {
+ pipe_io_destroy(chrc->write_io);
+ chrc->write_io = NULL;
+ g_dbus_emit_property_changed(btd_get_dbus_connection(),
+ chrc->path,
+ GATT_CHARACTERISTIC_IFACE,
+ "WriteAcquired");
+ } else if (chrc->notify_io) {
+ pipe_io_destroy(chrc->notify_io);
+ chrc->notify_io = NULL;
+ g_dbus_emit_property_changed(btd_get_dbus_connection(),
+ chrc->path,
+ GATT_CHARACTERISTIC_IFACE,
+ "NotifyAcquired");
+ }
+}
+
+static bool characteristic_pipe_hup(struct io *io, void *user_data)
+{
+ struct characteristic *chrc = user_data;
+
+ DBG("%s: io %p", chrc->path, io);
+
+ characteristic_destroy_pipe(chrc, io);
+
+ return false;
+}
+
+static DBusMessage *characteristic_create_pipe(struct characteristic *chrc,
+ DBusMessage *msg)
+{
+ struct bt_gatt_client *gatt = chrc->service->client->gatt;
+ int pipefd[2];
+ struct io *io;
+ bool dir;
+ uint16_t mtu;
+ DBusMessage *reply;
+
+ if (!gatt || !bt_gatt_client_is_ready(gatt))
+ return btd_error_failed(msg, "Not connected");
+
+ if (pipe2(pipefd, O_DIRECT | O_NONBLOCK | O_CLOEXEC) < 0)
+ return btd_error_failed(msg, strerror(errno));
+
+ dir = dbus_message_has_member(msg, "AcquireWrite");
+
+ io = io_new(pipefd[!dir]);
+ if (!io) {
+ close(pipefd[0]);
+ close(pipefd[1]);
+ return btd_error_failed(msg, strerror(EIO));
+ }
+
+ io_set_close_on_destroy(io, true);
+
+ if (!io_set_read_handler(io, chrc_pipe_read, chrc, NULL))
+ goto fail;
+
+ if (!io_set_disconnect_handler(io, characteristic_pipe_hup, chrc, NULL))
+ goto fail;
+
+ mtu = bt_gatt_client_get_mtu(gatt);
+
+ reply = g_dbus_create_reply(msg, DBUS_TYPE_UNIX_FD, &pipefd[dir],
+ DBUS_TYPE_UINT16, &mtu,
+ DBUS_TYPE_INVALID);
+
+ close(pipefd[dir]);
+
+ if (dir) {
+ chrc->write_io->io = io;
+ g_dbus_emit_property_changed(btd_get_dbus_connection(),
+ chrc->path,
+ GATT_CHARACTERISTIC_IFACE,
+ "WriteAcquired");
+ } else {
+ chrc->notify_io->io = io;
+ g_dbus_emit_property_changed(btd_get_dbus_connection(),
+ chrc->path,
+ GATT_CHARACTERISTIC_IFACE,
+ "NotifyAcquired");
+ }
+
+ queue_push_tail(chrc->service->client->ios, io);
+
+ DBG("%s: sender %s io %p", dbus_message_get_member(msg),
+ dbus_message_get_sender(msg), io);
+
+ return reply;
+
+fail:
+ io_destroy(io);
+ close(pipefd[dir]);
+ return btd_error_failed(msg, strerror(EIO));
+}
+
+static void characteristic_ready(bool success, uint8_t ecode, void *user_data)
+{
+ struct characteristic *chrc = user_data;
+ DBusMessage *reply;
+
+ chrc->ready_id = 0;
+
+ if (chrc->write_io && chrc->write_io->msg) {
+ reply = characteristic_create_pipe(chrc, chrc->write_io->msg);
+
+ g_dbus_send_message(btd_get_dbus_connection(), reply);
+
+ dbus_message_unref(chrc->write_io->msg);
+ chrc->write_io->msg = NULL;
+ }
+
+ if (chrc->notify_io && chrc->notify_io->msg) {
+ reply = characteristic_create_pipe(chrc, chrc->notify_io->msg);
+
+ g_dbus_send_message(btd_get_dbus_connection(), reply);
+
+ dbus_message_unref(chrc->notify_io->msg);
+ chrc->notify_io->msg = NULL;
+ }
+}
+
+static DBusMessage *characteristic_acquire_write(DBusConnection *conn,
+ DBusMessage *msg, void *user_data)
+{
+ struct characteristic *chrc = user_data;
+ struct bt_gatt_client *gatt = chrc->service->client->gatt;
+
+ if (!gatt)
+ return btd_error_failed(msg, "Not connected");
+
+ if (chrc->write_io)
+ return btd_error_not_permitted(msg, "Write acquired");
+
+ if (!(chrc->props & BT_GATT_CHRC_PROP_WRITE_WITHOUT_RESP))
+ return btd_error_not_supported(msg);
+
+ chrc->write_io = new0(struct pipe_io, 1);
+
+ if (!bt_gatt_client_is_ready(gatt)) {
+ /* GATT not ready, wait until it becomes ready */
+ if (!chrc->ready_id)
+ chrc->ready_id = bt_gatt_client_ready_register(gatt,
+ characteristic_ready,
+ chrc, NULL);
+ chrc->write_io->msg = dbus_message_ref(msg);
+ return NULL;
+ }
+
+ return characteristic_create_pipe(chrc, msg);
+}
+
#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
static DBusMessage *characteristic_write_value_by_type(DBusConnection *conn,
DBusMessage *msg, void *user_data)
create_notify_reply(op, true, 0);
}
+static void notify_io_cb(uint16_t value_handle, const uint8_t *value,
+ uint16_t length, void *user_data)
+{
+ struct iovec iov;
+ struct notify_client *client = user_data;
+ struct characteristic *chrc = client->chrc;
+ int err;
+
+ /* Drop notification if the pipe is not ready */
+ if (!chrc->notify_io->io)
+ return;
+
+ iov.iov_base = (void *) value;
+ iov.iov_len = length;
+
+ err = io_send(chrc->notify_io->io, &iov, 1);
+ if (err < 0)
+ error("io_send: %s", strerror(-err));
+}
+
+static void register_notify_io_cb(uint16_t att_ecode, void *user_data)
+{
+ struct notify_client *client = user_data;
+ struct characteristic *chrc = client->chrc;
+ struct bt_gatt_client *gatt = chrc->service->client->gatt;
+
+ if (att_ecode) {
+ queue_remove(chrc->notify_clients, client);
+ notify_client_free(client);
+ return;
+ }
+
+ if (!bt_gatt_client_is_ready(gatt)) {
+ if (!chrc->ready_id)
+ chrc->ready_id = bt_gatt_client_ready_register(gatt,
+ characteristic_ready,
+ chrc, NULL);
+ return;
+ }
+
+ characteristic_ready(true, 0, chrc);
+}
+
+static void notify_io_destroy(void *data)
+{
+ struct notify_client *client = data;
+
+ if (queue_remove(client->chrc->notify_clients, client))
+ notify_client_unref(client);
+}
+
+static DBusMessage *characteristic_acquire_notify(DBusConnection *conn,
+ DBusMessage *msg, void *user_data)
+{
+ struct characteristic *chrc = user_data;
+ struct bt_gatt_client *gatt = chrc->service->client->gatt;
+ const char *sender = dbus_message_get_sender(msg);
+ struct notify_client *client;
+
+ if (!gatt)
+ return btd_error_failed(msg, "Not connected");
+
+ if (chrc->notify_io)
+ return btd_error_not_permitted(msg, "Notify acquired");
+
+ /* Each client can only have one active notify session. */
+ if (!queue_isempty(chrc->notify_clients))
+ return btd_error_in_progress(msg);
+
+ if (!(chrc->props & BT_GATT_CHRC_PROP_NOTIFY))
+ return btd_error_not_supported(msg);
+
+ client = notify_client_create(chrc, sender);
+ if (!client)
+ return btd_error_failed(msg, "Failed allocate notify session");
+
+ client->notify_id = bt_gatt_client_register_notify(gatt,
+ chrc->value_handle,
+ register_notify_io_cb,
+ notify_io_cb,
+ client, NULL);
+ if (!client->notify_id)
+ return btd_error_failed(msg, "Failed to subscribe");
+
+ queue_push_tail(chrc->notify_clients, client);
+
+ chrc->notify_io = new0(struct pipe_io, 1);
+ chrc->notify_io->data = client;
+ chrc->notify_io->msg = dbus_message_ref(msg);
+ chrc->notify_io->destroy = notify_io_destroy;
+
+ return NULL;
+}
+
static DBusMessage *characteristic_start_notify(DBusConnection *conn,
DBusMessage *msg, void *user_data)
{
struct async_dbus_op *op;
struct notify_client *client;
+ if (chrc->notify_io)
+ return btd_error_not_permitted(msg, "Notify acquired");
+
if (!(chrc->props & BT_GATT_CHRC_PROP_NOTIFY ||
chrc->props & BT_GATT_CHRC_PROP_INDICATE))
return btd_error_not_supported(msg);
client = queue_find(chrc->notify_clients, match_notify_sender, sender);
if (client)
return client->notify_id ?
- btd_error_failed(msg, "Already notifying") :
+ g_dbus_create_reply(msg, DBUS_TYPE_INVALID) :
btd_error_in_progress(msg);
client = notify_client_create(chrc, sender);
if (!client)
return btd_error_failed(msg, "No notify session started");
+ if (chrc->notify_io) {
+ characteristic_destroy_pipe(chrc, chrc->notify_io->io);
+ return dbus_message_new_method_return(msg);
+ }
+
queue_remove(chrc->service->client->all_notify_clients, client);
bt_gatt_client_unregister_notify(gatt, client->notify_id);
update_notifying(chrc);
{ "Notifying", "b", characteristic_get_notifying, NULL,
characteristic_notifying_exists },
{ "Flags", "as", characteristic_get_flags, NULL, NULL },
+ { "WriteAcquired", "b", characteristic_get_write_acquired, NULL,
+ characteristic_write_acquired_exists },
+ { "NotifyAcquired", "b", characteristic_get_notify_acquired, NULL,
+ characteristic_notify_acquired_exists },
#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
{ "Descriptors", "ao", characteristic_get_descriptors },
#endif
{ "options", "a{sv}" }),
NULL,
characteristic_write_value) },
+ { GDBUS_ASYNC_METHOD("AcquireWrite",
+ GDBUS_ARGS({ "options", "a{sv}" }),
+ GDBUS_ARGS({ "fd", "h" },
+ { "mtu", "q" }),
+ characteristic_acquire_write) },
+ { GDBUS_ASYNC_METHOD("AcquireNotify",
+ GDBUS_ARGS({ "options", "a{sv}" }),
+ GDBUS_ARGS({ "fd", "h" },
+ { "mtu", "q" }),
+ characteristic_acquire_notify) },
#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
{ GDBUS_ASYNC_METHOD("WriteValuebyType",
GDBUS_ARGS({ "type", "y" }, { "value", "ay" },
queue_destroy(chrc->descs, NULL);
queue_destroy(chrc->notify_clients, NULL);
+ if (chrc->write_io) {
+ queue_remove(chrc->service->client->ios, chrc->write_io->io);
+ pipe_io_destroy(chrc->write_io);
+ }
+
+ if (chrc->notify_io) {
+ queue_remove(chrc->service->client->ios, chrc->notify_io->io);
+ pipe_io_destroy(chrc->notify_io);
+ }
+
g_free(chrc->path);
free(chrc);
}
return TRUE;
}
+static void append_incl_service_path(void *data, void *user_data)
+{
+ struct service *incl_service = data;
+ DBusMessageIter *array = user_data;
+
+ dbus_message_iter_append_basic(array, DBUS_TYPE_OBJECT_PATH,
+ &incl_service->path);
+}
+
+static gboolean service_get_includes(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct service *service = data;
+ DBusMessageIter array;
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "{o}", &array);
+
+ queue_foreach(service->incl_services, append_incl_service_path, &array);
+
+ dbus_message_iter_close_container(iter, &array);
+
+ return TRUE;
+
+}
+
#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
static void append_chrc_path(void *data, void *user_data)
{
{ "UUID", "s", service_get_uuid },
{ "Device", "o", service_get_device },
{ "Primary", "b", service_get_primary },
+ { "Includes", "ao", service_get_includes },
#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
{ "Characteristics", "ao", service_get_characteristics },
#endif
struct service *service = data;
queue_destroy(service->chrcs, NULL); /* List should be empty here */
+ queue_destroy(service->incl_services, NULL);
g_free(service->path);
free(service);
}
service = new0(struct service, 1);
service->chrcs = queue_new();
+ service->incl_services = queue_new();
service->client = client;
gatt_db_attribute_get_service_data(attr, &service->start_handle,
return service;
}
+static void on_service_removed(void *data, void *user_data)
+{
+ struct service *service = data;
+ struct service *removed_service = user_data;
+
+ if (queue_remove(service->incl_services, removed_service))
+ g_dbus_emit_property_changed(btd_get_dbus_connection(),
+ service->path,
+ GATT_SERVICE_IFACE,
+ "Includes");
+}
+
static void unregister_service(void *data)
{
struct service *service = data;
+ struct btd_gatt_client *client = service->client;
DBG("Removing GATT service: %s", service->path);
#endif
queue_remove_all(service->chrcs, NULL, NULL, unregister_characteristic);
+ queue_remove_all(service->incl_services, NULL, NULL, NULL);
+
+ queue_foreach(client->services, on_service_removed, service);
g_dbus_unregister_interface(btd_get_dbus_connection(), service->path,
GATT_SERVICE_IFACE);
gatt_db_service_foreach_char(attr, export_char, &data);
- if (data.failed)
- return false;
-
- return true;
+ return !data.failed;
}
+
#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
static gboolean set_chrcs_ready(gpointer user_data)
{
#endif
}
+static bool match_service_handle(const void *a, const void *b)
+{
+ const struct service *service = a;
+ uint16_t start_handle = PTR_TO_UINT(b);
+
+ return service->start_handle == start_handle;
+}
+
+struct update_incl_data {
+ struct service *service;
+ bool changed;
+};
+
+static void update_included_service(struct gatt_db_attribute *attrib,
+ void *user_data)
+{
+ struct update_incl_data *update_data = user_data;
+ struct btd_gatt_client *client = update_data->service->client;
+ struct service *service = update_data->service;
+ struct service *incl_service;
+ uint16_t start_handle;
+
+ gatt_db_attribute_get_incl_data(attrib, NULL, &start_handle, NULL);
+
+ incl_service = queue_find(client->services, match_service_handle,
+ UINT_TO_PTR(start_handle));
+
+ if (!incl_service)
+ return;
+
+ /* Check if service is already on list */
+ if (queue_find(service->incl_services, NULL, incl_service))
+ return;
+
+ queue_push_tail(service->incl_services, incl_service);
+ update_data->changed = true;
+}
+
+static void update_included_services(void *data, void *user_data)
+{
+ struct btd_gatt_client *client = user_data;
+ struct service *service = data;
+ struct gatt_db_attribute *attr;
+ struct update_incl_data inc_data = {
+ .changed = false,
+ .service = service,
+ };
+
+ attr = gatt_db_get_attribute(client->db, service->start_handle);
+ gatt_db_service_foreach_incl(attr, update_included_service, &inc_data);
+
+ if (inc_data.changed)
+ g_dbus_emit_property_changed(btd_get_dbus_connection(),
+ service->path,
+ GATT_SERVICE_IFACE,
+ "Includes");
+}
+
static void create_services(struct btd_gatt_client *client)
{
DBG("Exporting objects for GATT services: %s", client->devaddr);
gatt_db_foreach_service(client->db, NULL, export_service, client);
+
+ queue_foreach(client->services, update_included_services, client);
}
struct btd_gatt_client *btd_gatt_client_new(struct btd_device *device)
client = new0(struct btd_gatt_client, 1);
client->services = queue_new();
client->all_notify_clients = queue_new();
+ client->ios = queue_new();
client->device = device;
ba2str(device_get_address(device), client->devaddr);
#endif
queue_destroy(client->services, unregister_service);
queue_destroy(client->all_notify_clients, NULL);
+ queue_destroy(client->ios, NULL);
bt_gatt_client_unref(client->gatt);
gatt_db_unref(client->db);
free(client);
return;
export_service(attrib, client);
-}
-
-static bool match_service_handle(const void *a, const void *b)
-{
- const struct service *service = a;
- uint16_t start_handle = PTR_TO_UINT(b);
- return service->start_handle == start_handle;
+ queue_foreach(client->services, update_included_services, client);
}
void btd_gatt_client_service_removed(struct btd_gatt_client *client,
client->wait_charcs_id = 0;
}
#endif
+ queue_remove_all(client->ios, NULL, NULL,
+ (queue_destroy_func_t) io_shutdown);
/*
* TODO: Once GATT over BR/EDR is properly supported, we should pass the
#include <stdint.h>
#include <stdlib.h>
#include <errno.h>
+#include <unistd.h>
#include "lib/bluetooth.h"
#include "lib/sdp.h"
#include "gdbus/gdbus.h"
#include "src/shared/util.h"
#include "src/shared/queue.h"
+#include "src/shared/io.h"
#include "src/shared/att.h"
#include "src/shared/gatt-db.h"
#include "src/shared/gatt-server.h"
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#endif
+struct gatt_record {
+ struct btd_gatt_database *database;
+ uint32_t handle;
+ struct gatt_db_attribute *attr;
+};
+
struct btd_gatt_database {
struct btd_adapter *adapter;
struct gatt_db *db;
unsigned int db_id;
GIOChannel *le_io;
GIOChannel *l2cap_io;
- uint32_t gap_handle;
- uint32_t gatt_handle;
+ struct queue *records;
struct queue *device_states;
struct queue *ccc_callbacks;
struct gatt_db_attribute *svc_chngd;
uint8_t props;
uint8_t ext_props;
uint32_t perm;
+ uint16_t mtu;
+ struct io *write_io;
+ struct io *notify_io;
struct gatt_db_attribute *attrib;
struct gatt_db_attribute *ccc;
struct queue *pending_reads;
struct pending_op {
struct btd_device *device;
unsigned int id;
-#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
uint16_t offset;
-#endif
+ uint8_t link_type;
struct gatt_db_attribute *attrib;
struct queue *owner_queue;
struct iovec data;
};
struct device_state {
+ struct btd_gatt_database *db;
bdaddr_t bdaddr;
uint8_t bdaddr_type;
+ unsigned int disc_id;
struct queue *ccc_states;
};
-typedef uint8_t (*btd_gatt_database_ccc_write_t) (uint16_t value,
+typedef uint8_t (*btd_gatt_database_ccc_write_t) (struct bt_att *att,
+ uint16_t value,
void *user_data);
typedef void (*btd_gatt_database_destroy_t) (void *data);
return queue_find(database->device_states, dev_state_match, &dev_info);
}
+static bool ccc_state_match(const void *a, const void *b)
+{
+ const struct ccc_state *ccc = a;
+ uint16_t handle = PTR_TO_UINT(b);
+
+ return ccc->handle == handle;
+}
+
+static struct ccc_state *find_ccc_state(struct device_state *dev_state,
+ uint16_t handle)
+{
+ return queue_find(dev_state->ccc_states, ccc_state_match,
+ UINT_TO_PTR(handle));
+}
+
+static struct device_state *device_state_create(struct btd_gatt_database *db,
+ bdaddr_t *bdaddr,
+ uint8_t bdaddr_type)
+{
+ struct device_state *dev_state;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ char addr[18] = { 0 };
+
+ ba2str(bdaddr, addr);
+ DBG("create device_state for %s [%d]", addr, bdaddr_type);
+#endif
+
+ dev_state = new0(struct device_state, 1);
+ dev_state->db = db;
+ dev_state->ccc_states = queue_new();
+ bacpy(&dev_state->bdaddr, bdaddr);
+ dev_state->bdaddr_type = bdaddr_type;
+
+ return dev_state;
+}
+
+static void device_state_free(void *data)
+{
+ struct device_state *state = data;
+
+ queue_destroy(state->ccc_states, free);
+ free(state);
+}
+
+static void clear_ccc_state(void *data, void *user_data)
+{
+ struct ccc_state *ccc = data;
+ struct btd_gatt_database *db = user_data;
+ struct ccc_cb_data *ccc_cb;
+
+ if (!ccc->value[0])
+ return;
+
+ ccc_cb = queue_find(db->ccc_callbacks, ccc_cb_match_handle,
+ UINT_TO_PTR(ccc->handle));
+ if (!ccc_cb)
+ return;
+
+ if (ccc_cb->callback)
+ ccc_cb->callback(NULL, 0, ccc_cb->user_data);
+}
+
+static void att_disconnected(int err, void *user_data)
+{
+ struct device_state *state = user_data;
+ struct btd_device *device;
+
+ DBG("");
+
+ state->disc_id = 0;
+
+ device = btd_adapter_get_device(state->db->adapter, &state->bdaddr,
+ state->bdaddr_type);
+ if (!device)
+ goto remove;
+
+ if (device_is_paired(device, state->bdaddr_type))
+ return;
+
+remove:
+ /* Remove device state if device no longer exists or is not paired */
+ if (queue_remove(state->db->device_states, state)) {
+ queue_foreach(state->ccc_states, clear_ccc_state, state->db);
+ device_state_free(state);
+ }
+}
+
+static bool get_dst_info(struct bt_att *att, bdaddr_t *dst, uint8_t *dst_type)
+{
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ return bt_att_get_remote_addr(att, dst, dst_type);
+#else
+ GIOChannel *io = NULL;
+ GError *gerr = NULL;
+
+ io = g_io_channel_unix_new(bt_att_get_fd(att));
+ if (!io)
+ return false;
+
+ bt_io_get(io, &gerr, BT_IO_OPT_DEST_BDADDR, dst,
+ BT_IO_OPT_DEST_TYPE, dst_type,
+ BT_IO_OPT_INVALID);
+ if (gerr) {
+ error("gatt: bt_io_get: %s", gerr->message);
+ g_error_free(gerr);
+ g_io_channel_unref(io);
+ return false;
+ }
+
+ g_io_channel_unref(io);
+
+ return true;
+#endif
+}
+
#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
static void send_service_changed_indication_on_reconnect(
struct btd_device *device,
}
#endif
-static bool ccc_state_match(const void *a, const void *b)
-{
- const struct ccc_state *ccc = a;
- uint16_t handle = PTR_TO_UINT(b);
-
- return ccc->handle == handle;
-}
-
-static struct ccc_state *find_ccc_state(struct device_state *dev_state,
- uint16_t handle)
-{
- return queue_find(dev_state->ccc_states, ccc_state_match,
- UINT_TO_PTR(handle));
-}
-
-static struct device_state *device_state_create(bdaddr_t *bdaddr,
- uint8_t bdaddr_type)
-{
- struct device_state *dev_state;
-#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
- char addr[18] = { 0 };
-
- ba2str(bdaddr, addr);
- DBG("create device_state for %s [%d]", addr, bdaddr_type);
-#endif
-
- dev_state = new0(struct device_state, 1);
- dev_state->ccc_states = queue_new();
- bacpy(&dev_state->bdaddr, bdaddr);
- dev_state->bdaddr_type = bdaddr_type;
-
- return dev_state;
-}
-
static struct device_state *get_device_state(struct btd_gatt_database *database,
- bdaddr_t *bdaddr,
- uint8_t bdaddr_type)
+ struct bt_att *att)
{
struct device_state *dev_state;
+ bdaddr_t bdaddr;
+ uint8_t bdaddr_type;
+
+ if (!get_dst_info(att, &bdaddr, &bdaddr_type))
+ return NULL;
/*
* Find and return a device state. If a matching state doesn't exist,
* then create a new one.
*/
- dev_state = find_device_state(database, bdaddr, bdaddr_type);
+ dev_state = find_device_state(database, &bdaddr, bdaddr_type);
if (dev_state)
- return dev_state;
+ goto done;
- dev_state = device_state_create(bdaddr, bdaddr_type);
+ dev_state = device_state_create(database, &bdaddr, bdaddr_type);
queue_push_tail(database->device_states, dev_state);
+done:
+ if (!dev_state->disc_id)
+ dev_state->disc_id = bt_att_register_disconnect(att,
+ att_disconnected,
+ dev_state, NULL);
+
return dev_state;
}
static struct ccc_state *get_ccc_state(struct btd_gatt_database *database,
- bdaddr_t *bdaddr,
- uint8_t bdaddr_type,
- uint16_t handle)
+ struct bt_att *att, uint16_t handle)
{
struct device_state *dev_state;
struct ccc_state *ccc;
-#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
- char addr[18] = { 0 };
-#endif
- dev_state = get_device_state(database, bdaddr, bdaddr_type);
+ dev_state = get_device_state(database, att);
+ if (!dev_state)
+ return NULL;
ccc = find_ccc_state(dev_state, handle);
if (ccc)
return ccc;
-#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
- ba2str(bdaddr, addr);
- DBG("create ccc_state of handle: 0x%04x for %s [%d]",
- handle, addr, bdaddr_type);
-#endif
-
ccc = new0(struct ccc_state, 1);
ccc->handle = handle;
queue_push_tail(dev_state->ccc_states, ccc);
return ccc;
}
-static void device_state_free(void *data)
-{
- struct device_state *state = data;
-
- queue_destroy(state->ccc_states, free);
- free(state);
-}
-
static void cancel_pending_read(void *data)
{
struct pending_op *op = data;
{
struct external_chrc *chrc = data;
+ io_destroy(chrc->write_io);
+ io_destroy(chrc->notify_io);
+
queue_destroy(chrc->pending_reads, cancel_pending_read);
queue_destroy(chrc->pending_writes, cancel_pending_write);
free(app);
}
+static void gatt_record_free(void *data)
+{
+ struct gatt_record *rec = data;
+
+ adapter_service_remove(rec->database->adapter, rec->handle);
+ free(rec);
+}
+
static void gatt_database_free(void *data)
{
struct btd_gatt_database *database = data;
g_io_channel_unref(database->l2cap_io);
}
- if (database->gatt_handle)
- adapter_service_remove(database->adapter,
- database->gatt_handle);
-
- if (database->gap_handle)
- adapter_service_remove(database->adapter, database->gap_handle);
-
/* TODO: Persistently store CCC states before freeing them */
gatt_db_unregister(database->db, database->db_id);
+ queue_destroy(database->records, gatt_record_free);
queue_destroy(database->device_states, device_state_free);
queue_destroy(database->apps, app_free);
queue_destroy(database->profiles, profile_free);
return record;
}
-static uint32_t database_add_record(struct btd_gatt_database *database,
- uint16_t uuid,
- struct gatt_db_attribute *attr,
- const char *name)
+static void database_add_record(struct btd_gatt_database *database,
+ struct gatt_db_attribute *attr)
{
+ struct gatt_record *rec;
sdp_record_t *record;
uint16_t start, end;
uuid_t svc, gap_uuid;
+ bt_uuid_t uuid;
+ const char *name = NULL;
+ char uuidstr[MAX_LEN_UUID_STR];
+
+ gatt_db_attribute_get_service_uuid(attr, &uuid);
+
+ switch (uuid.type) {
+ case BT_UUID16:
+ name = bt_uuid16_to_str(uuid.value.u16);
+ sdp_uuid16_create(&svc, uuid.value.u16);
+ break;
+ case BT_UUID32:
+ name = bt_uuid32_to_str(uuid.value.u32);
+ sdp_uuid32_create(&svc, uuid.value.u32);
+ break;
+ case BT_UUID128:
+ bt_uuid_to_string(&uuid, uuidstr, sizeof(uuidstr));
+ name = bt_uuidstr_to_str(uuidstr);
+ sdp_uuid128_create(&svc, (void *) &uuid.value.u128);
+ break;
+ case BT_UUID_UNSPEC:
+ return;
+ }
- sdp_uuid16_create(&svc, uuid);
gatt_db_attribute_get_service_handles(attr, &start, &end);
record = record_new(&svc, start, end);
if (!record)
- return 0;
+ return;
if (name != NULL)
#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
}
#endif
- if (adapter_service_add(database->adapter, record) == 0)
- return record->handle;
+ if (adapter_service_add(database->adapter, record) < 0) {
+ sdp_record_free(record);
+ return;
+ }
- sdp_record_free(record);
- return 0;
+ rec = new0(struct gatt_record, 1);
+ rec->database = database;
+ rec->handle = record->handle;
+ rec->attr = attr;
+ queue_push_tail(database->records, rec);
}
static void populate_gap_service(struct btd_gatt_database *database)
/* Add the GAP service */
bt_uuid16_create(&uuid, UUID_GAP);
service = gatt_db_add_service(database->db, &uuid, true, 5);
- database->gap_handle = database_add_record(database, UUID_GAP, service,
- "Generic Access Profile");
/*
* Device Name characteristic.
gatt_db_service_set_active(service, true);
}
-static bool get_dst_info(struct bt_att *att, bdaddr_t *dst, uint8_t *dst_type)
-{
-#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
- return bt_att_get_remote_addr(att, dst, dst_type);
-#else
- GIOChannel *io = NULL;
- GError *gerr = NULL;
-
- io = g_io_channel_unix_new(bt_att_get_fd(att));
- if (!io)
- return false;
-
- bt_io_get(io, &gerr, BT_IO_OPT_DEST_BDADDR, dst,
- BT_IO_OPT_DEST_TYPE, dst_type,
- BT_IO_OPT_INVALID);
- if (gerr) {
- error("gatt: bt_io_get: %s", gerr->message);
- g_error_free(gerr);
- g_io_channel_unref(io);
- return false;
- }
-
- g_io_channel_unref(io);
-
- return true;
-#endif
-}
-
static void gatt_ccc_read_cb(struct gatt_db_attribute *attrib,
unsigned int id, uint16_t offset,
uint8_t opcode, struct bt_att *att,
uint8_t ecode = 0;
const uint8_t *value = NULL;
size_t len = 0;
- bdaddr_t bdaddr;
- uint8_t bdaddr_type;
handle = gatt_db_attribute_get_handle(attrib);
goto done;
}
- if (!get_dst_info(att, &bdaddr, &bdaddr_type)) {
+ ccc = get_ccc_state(database, att, handle);
+ if (!ccc) {
ecode = BT_ATT_ERROR_UNLIKELY;
goto done;
}
- ccc = get_ccc_state(database, &bdaddr, bdaddr_type, handle);
-
len = 2 - offset;
value = len ? &ccc->value[offset] : NULL;
struct ccc_cb_data *ccc_cb;
uint16_t handle;
uint8_t ecode = 0;
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
bdaddr_t bdaddr;
- uint8_t bdaddr_type;
+ uint8_t bdaddr_type;
+#endif
handle = gatt_db_attribute_get_handle(attrib);
goto done;
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
if (!get_dst_info(att, &bdaddr, &bdaddr_type)) {
+ ecode = BT_ATT_ERROR_UNLIKELY;
+ goto done;
+ }
+#endif
+
+ ccc = get_ccc_state(database, att, handle);
+ if (!ccc) {
ecode = BT_ATT_ERROR_UNLIKELY;
goto done;
}
- ccc = get_ccc_state(database, &bdaddr, bdaddr_type, handle);
-
ccc_cb = queue_find(database->ccc_callbacks, ccc_cb_match_handle,
UINT_TO_PTR(gatt_db_attribute_get_handle(attrib)));
if (!ccc_cb) {
goto done;
if (ccc_cb->callback)
- ecode = ccc_cb->callback(get_le16(value), ccc_cb->user_data);
+ ecode = ccc_cb->callback(att, get_le16(value),
+ ccc_cb->user_data);
if (!ecode) {
ccc->value[0] = value[0];
/* Add the GATT service */
bt_uuid16_create(&uuid, UUID_GATT);
service = gatt_db_add_service(database->db, &uuid, true, 4);
- database->gatt_handle = database_add_record(database, UUID_GATT,
- service,
- "Generic Attribute Profile");
bt_uuid16_create(&uuid, GATT_CHARAC_SERVICE_CHANGED);
database->svc_chngd = gatt_db_service_add_characteristic(service, &uuid,
const uint8_t *value;
uint16_t len;
bool indicate;
+ GDBusProxy *proxy;
};
#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
static void conf_cb(void *user_data)
{
+ GDBusProxy *proxy = user_data;
DBG("GATT server received confirmation");
+
+ if (proxy != NULL)
+ {
+ g_dbus_proxy_method_call(proxy, "Confirm", NULL, NULL, NULL, NULL);
+ }
+
#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
struct notify_indicate_cb *confirm = user_data;
DBG("GATT server sending indication");
bt_gatt_server_send_indication(server, notify->handle, notify->value,
notify->len, conf_cb,
- NULL, NULL);
+ notify->proxy, NULL);
return;
remove:
/* Remove device state if device no longer exists or is not paired */
- if (queue_remove(notify->database->device_states, device_state))
+ if (queue_remove(notify->database->device_states, device_state)) {
+ queue_foreach(device_state->ccc_states, clear_ccc_state,
+ notify->database);
device_state_free(device_state);
+ }
}
static void send_notification_to_devices(struct btd_gatt_database *database,
uint16_t handle, const uint8_t *value,
uint16_t len, uint16_t ccc_handle,
- bool indicate)
+ bool indicate, GDBusProxy *proxy)
{
struct notify notify;
notify.value = value;
notify.len = len;
notify.indicate = indicate;
+ notify.proxy = proxy;
queue_foreach(database->device_states, send_notification_to_device,
¬ify);
put_le16(end, value + 2);
send_notification_to_devices(database, handle, value, sizeof(value),
- ccc_handle, true);
+ ccc_handle, true, NULL);
}
static void gatt_db_service_added(struct gatt_db_attribute *attrib,
DBG("GATT Service added to local database");
+ database_add_record(database, attrib);
+
send_service_changed(database, attrib);
}
queue_remove_all(state->ccc_states, ccc_match_service, user_data, free);
}
+static bool match_gatt_record(const void *data, const void *user_data)
+{
+ const struct gatt_record *rec = data;
+ const struct gatt_db_attribute *attr = user_data;
+
+ return (rec->attr == attr);
+}
+
static void gatt_db_service_removed(struct gatt_db_attribute *attrib,
void *user_data)
{
struct btd_gatt_database *database = user_data;
+ struct gatt_record *rec;
DBG("Local GATT service removed");
+ rec = queue_remove_if(database->records, match_gatt_record, attrib);
+ if (rec)
+ gatt_record_free(rec);
+
send_service_changed(database, attrib);
queue_foreach(database->device_states, remove_device_ccc, attrib);
#else
static uint8_t dbus_error_to_att_ecode(const char *error_name)
{
+
if (strcmp(error_name, "org.bluez.Error.Failed") == 0)
return 0x80; /* For now return this "application error" */
static void read_reply_cb(DBusMessage *message, void *user_data)
{
struct pending_op *op = user_data;
-
DBusError err;
DBusMessageIter iter, array;
uint8_t ecode = 0;
free(op);
}
-#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
-static struct pending_op *pending_read_new(struct btd_device *device,
- struct queue *owner_queue,
- struct gatt_db_attribute *attrib,
- unsigned int id, uint16_t offset)
-#else
static struct pending_op *pending_read_new(struct btd_device *device,
struct queue *owner_queue,
struct gatt_db_attribute *attrib,
- unsigned int id)
-#endif
+ unsigned int id, uint16_t offset,
+ uint8_t link_type)
{
struct pending_op *op;
op->device = device;
op->attrib = attrib;
op->id = id;
-#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
op->offset = offset;
-#endif
+ op->link_type = link_type;
queue_push_tail(owner_queue, op);
return op;
}
-#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
static void append_options(DBusMessageIter *iter, void *user_data)
{
struct pending_op *op = user_data;
const char *path = device_get_path(op->device);
+ const char *link;
+
+ switch (op->link_type) {
+ case BT_ATT_LINK_BREDR:
+ link = "BR/EDR";
+ break;
+ case BT_ATT_LINK_LE:
+ link = "LE";
+ break;
+ default:
+ link = NULL;
+ break;
+ }
dict_append_entry(iter, "device", DBUS_TYPE_OBJECT_PATH, &path);
+ if (op->offset)
+ dict_append_entry(iter, "offset", DBUS_TYPE_UINT16,
+ &op->offset);
+ if (link)
+ dict_append_entry(iter, "link", DBUS_TYPE_STRING, &link);
}
-#endif
static void read_setup_cb(DBusMessageIter *iter, void *user_data)
{
#endif
}
-#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
static struct pending_op *send_read(struct btd_device *device,
struct gatt_db_attribute *attrib,
GDBusProxy *proxy,
struct queue *owner_queue,
unsigned int id,
- uint16_t offset)
-#else
-static struct pending_op *send_read(struct btd_device *device,
- struct gatt_db_attribute *attrib,
- GDBusProxy *proxy,
- struct queue *owner_queue,
- unsigned int id)
-#endif
+ uint16_t offset,
+ uint8_t link_type)
{
struct pending_op *op;
-#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
- op = pending_read_new(device, owner_queue, attrib, id, offset);
-#else
- op = pending_read_new(device, owner_queue, attrib, id);
-#endif
+
+ op = pending_read_new(device, owner_queue, attrib, id, offset,
+ link_type);
if (g_dbus_proxy_method_call(proxy, "ReadValue", read_setup_cb,
read_reply_cb, op, pending_op_free) == TRUE)
#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
{
DBusMessageIter dict;
-
dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
- DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
- DBUS_TYPE_STRING_AS_STRING
- DBUS_TYPE_VARIANT_AS_STRING
- DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
- &dict);
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING
+ DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+ &dict);
append_options(&dict, op);
static void write_reply_cb(DBusMessage *message, void *user_data)
{
struct pending_op *op = user_data;
-
DBusError err;
DBusMessageIter iter;
uint8_t ecode = 0;
done:
gatt_db_attribute_write_result(op->attrib, op->id, ecode);
}
-#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+
static struct pending_op *pending_write_new(struct btd_device *device,
struct queue *owner_queue,
struct gatt_db_attribute *attrib,
unsigned int id,
const uint8_t *value,
size_t len,
- uint16_t offset)
-#else
-static struct pending_op *pending_write_new(struct btd_device *device,
- struct queue *owner_queue,
- struct gatt_db_attribute *attrib,
- unsigned int id,
- const uint8_t *value,
- size_t len)
-#endif
+ uint16_t offset, uint8_t link_type)
{
struct pending_op *op;
op->owner_queue = owner_queue;
op->attrib = attrib;
op->id = id;
-#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
op->offset = offset;
-#endif
+ op->link_type = link_type;
queue_push_tail(owner_queue, op);
return op;
}
-#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+
static struct pending_op *send_write(struct btd_device *device,
+ struct gatt_db_attribute *attrib,
+ GDBusProxy *proxy,
+ struct queue *owner_queue,
+ unsigned int id,
+ const uint8_t *value, size_t len,
+ uint16_t offset, uint8_t link_type)
+{
+ struct pending_op *op;
+
+ op = pending_write_new(device, owner_queue, attrib, id, value, len,
+ offset, link_type);
+
+ if (g_dbus_proxy_method_call(proxy, "WriteValue", write_setup_cb,
+ owner_queue ? write_reply_cb : NULL,
+ op, pending_op_free) == TRUE)
+ return op;
+
+ pending_op_free(op);
+
+ return NULL;
+}
+
+static bool pipe_hup(struct io *io, void *user_data)
+{
+ struct external_chrc *chrc = user_data;
+
+ DBG("%p closed\n", io);
+
+ if (io == chrc->write_io)
+ chrc->write_io = NULL;
+ else
+ chrc->notify_io = NULL;
+
+ io_destroy(io);
+
+ return false;
+}
+
+static bool pipe_io_read(struct io *io, void *user_data)
+{
+ struct external_chrc *chrc = user_data;
+ uint8_t buf[512];
+ int fd = io_get_fd(io);
+ ssize_t bytes_read;
+
+ bytes_read = read(fd, buf, sizeof(buf));
+ if (bytes_read < 0)
+ return false;
+
+ send_notification_to_devices(chrc->service->app->database,
+ gatt_db_attribute_get_handle(chrc->attrib),
+ buf, bytes_read,
+ gatt_db_attribute_get_handle(chrc->ccc),
+ chrc->props & BT_GATT_CHRC_PROP_INDICATE,
+ chrc->proxy);
+
+ return true;
+}
+
+static struct io *pipe_io_new(int fd, void *user_data)
+{
+ struct io *io;
+
+ io = io_new(fd);
+
+ io_set_close_on_destroy(io, true);
+
+ io_set_read_handler(io, pipe_io_read, user_data, NULL);
+
+ io_set_disconnect_handler(io, pipe_hup, user_data, NULL);
+
+ return io;
+}
+
+static int pipe_io_send(struct io *io, const void *data, size_t len)
+{
+ struct iovec iov;
+
+ iov.iov_base = (void *) data;
+ iov.iov_len = len;
+
+ return io_send(io, &iov, 1);
+}
+
+static void acquire_write_reply(DBusMessage *message, void *user_data)
+{
+ struct pending_op *op = user_data;
+ struct external_chrc *chrc;
+ DBusError err;
+ int fd;
+ uint16_t mtu;
+
+ chrc = gatt_db_attribute_get_user_data(op->attrib);
+ dbus_error_init(&err);
+
+ if (dbus_set_error_from_message(&err, message) == TRUE) {
+ error("Failed to acquire write: %s\n", err.name);
+ dbus_error_free(&err);
+ goto retry;
+ }
+
+ if ((dbus_message_get_args(message, NULL, DBUS_TYPE_UNIX_FD, &fd,
+ DBUS_TYPE_UINT16, &mtu,
+ DBUS_TYPE_INVALID) == false)) {
+ error("Invalid AcquireWrite response\n");
+ goto retry;
+ }
+
+ DBG("AcquireWrite success: fd %d MTU %u\n", fd, mtu);
+
+ chrc->write_io = pipe_io_new(fd, chrc);
+
+ if (pipe_io_send(chrc->write_io, op->data.iov_base,
+ op->data.iov_len) < 0)
+ goto retry;
+
+ gatt_db_attribute_write_result(op->attrib, op->id, 0);
+
+ return;
+
+retry:
+ send_write(op->device, op->attrib, chrc->proxy, NULL, op->id,
+ op->data.iov_base, op->data.iov_len, 0,
+ op->link_type);
+}
+
+static void acquire_write_setup(DBusMessageIter *iter, void *user_data)
+{
+ struct pending_op *op = user_data;
+ DBusMessageIter dict;
+ struct bt_gatt_server *server;
+ uint16_t mtu;
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING
+ DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+ &dict);
+
+ append_options(&dict, op);
+
+ server = btd_device_get_gatt_server(op->device);
+
+ mtu = bt_gatt_server_get_mtu(server);
+
+ dict_append_entry(&dict, "MTU", DBUS_TYPE_UINT16, &mtu);
+
+ dbus_message_iter_close_container(iter, &dict);
+}
+
+static struct pending_op *acquire_write(struct external_chrc *chrc,
+ struct btd_device *device,
struct gatt_db_attribute *attrib,
- GDBusProxy *proxy,
- struct queue *owner_queue,
unsigned int id,
const uint8_t *value, size_t len,
- uint16_t offset)
-#else
-static struct pending_op *send_write(struct btd_device *device,
- struct gatt_db_attribute *attrib,
- GDBusProxy *proxy,
- struct queue *owner_queue,
- unsigned int id,
- const uint8_t *value, size_t len)
-#endif
+ uint8_t link_type)
{
struct pending_op *op;
-#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
- op = pending_write_new(device, owner_queue, attrib, id, value, len, offset);
-#else
- op = pending_write_new(device, owner_queue, attrib, id, value, len);
-#endif
- if (g_dbus_proxy_method_call(proxy, "WriteValue", write_setup_cb,
- owner_queue ? write_reply_cb : NULL,
- op, pending_op_free) == TRUE)
+ op = pending_write_new(device, NULL, attrib, id, value, len, 0,
+ link_type);
+
+ if (g_dbus_proxy_method_call(chrc->proxy, "AcquireWrite",
+ acquire_write_setup,
+ acquire_write_reply,
+ op, pending_op_free))
return op;
pending_op_free(op);
return NULL;
}
-static uint8_t ccc_write_cb(uint16_t value, void *user_data)
+static void acquire_notify_reply(DBusMessage *message, void *user_data)
{
struct external_chrc *chrc = user_data;
+ DBusError err;
+ int fd;
+ uint16_t mtu;
+
+ dbus_error_init(&err);
+
+ if (dbus_set_error_from_message(&err, message) == TRUE) {
+ error("Failed to acquire notify: %s\n", err.name);
+ dbus_error_free(&err);
+ goto retry;
+ }
+
+ if ((dbus_message_get_args(message, NULL, DBUS_TYPE_UNIX_FD, &fd,
+ DBUS_TYPE_UINT16, &mtu,
+ DBUS_TYPE_INVALID) == false)) {
+ error("Invalid AcquirNotify response\n");
+ goto retry;
+ }
+
+ DBG("AcquireNotify success: fd %d MTU %u\n", fd, mtu);
+
+ chrc->notify_io = pipe_io_new(fd, chrc);
+
+ __sync_fetch_and_add(&chrc->ntfy_cnt, 1);
+
+ return;
+
+retry:
+ g_dbus_proxy_method_call(chrc->proxy, "StartNotify", NULL, NULL,
+ NULL, NULL);
+
+ __sync_fetch_and_add(&chrc->ntfy_cnt, 1);
+}
+
+static void acquire_notify_setup(DBusMessageIter *iter, void *user_data)
+{
+ DBusMessageIter dict;
+ struct external_chrc *chrc = user_data;
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING
+ DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+ &dict);
+
+ dict_append_entry(&dict, "MTU", DBUS_TYPE_UINT16, &chrc->mtu);
+
+ dbus_message_iter_close_container(iter, &dict);
+}
+
+static uint8_t ccc_write_cb(struct bt_att *att, uint16_t value, void *user_data)
+{
+ struct external_chrc *chrc = user_data;
+ DBusMessageIter iter;
DBG("External CCC write received with value: 0x%04x", value);
if (__sync_sub_and_fetch(&chrc->ntfy_cnt, 1))
return 0;
+ if (chrc->notify_io) {
+ io_destroy(chrc->notify_io);
+ chrc->notify_io = NULL;
+ return 0;
+ }
+
/*
* Send request to stop notifying. This is best-effort
* operation, so simply ignore the return the value.
(value == 2 && !(chrc->props & BT_GATT_CHRC_PROP_INDICATE)))
return BT_ERROR_CCC_IMPROPERLY_CONFIGURED;
+ if (chrc->notify_io) {
+ __sync_fetch_and_add(&chrc->ntfy_cnt, 1);
+ return 0;
+ }
+
+ chrc->mtu = bt_att_get_mtu(att);
+
+ /* Make use of AcquireNotify if supported */
+ if (g_dbus_proxy_get_property(chrc->proxy, "NotifyAcquired", &iter)) {
+ if (g_dbus_proxy_method_call(chrc->proxy, "AcquireNotify",
+ acquire_notify_setup,
+ acquire_notify_reply,
+ chrc, NULL))
+ return 0;
+ }
+
/*
* Always call StartNotify for an incoming enable and ignore the return
* value for now.
*/
- if (g_dbus_proxy_method_call(chrc->proxy,
- "StartNotify", NULL, NULL,
+ if (g_dbus_proxy_method_call(chrc->proxy, "StartNotify", NULL, NULL,
NULL, NULL) == FALSE)
return BT_ATT_ERROR_UNLIKELY;
gatt_db_attribute_get_handle(chrc->attrib),
value, len,
gatt_db_attribute_get_handle(chrc->ccc),
- chrc->props & BT_GATT_CHRC_PROP_INDICATE);
+ chrc->props & BT_GATT_CHRC_PROP_INDICATE, proxy);
#endif
}
error("Unable to find device object");
goto fail;
}
-#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
- if (send_read(device, attrib, desc->proxy, desc->pending_reads, id, offset))
-#else
- if (send_read(device, attrib, desc->proxy, desc->pending_reads, id))
-#endif
+ if (send_read(device, attrib, desc->proxy, desc->pending_reads, id,
+ offset, bt_att_get_link_type(att)))
return;
-
fail:
gatt_db_attribute_read_result(attrib, id, BT_ATT_ERROR_UNLIKELY,
NULL, 0);
error("Unable to find device object");
goto fail;
}
-#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
- if (send_write(device, attrib, desc->proxy, desc->pending_writes, id,
- value, len, offset))
-#else
if (send_write(device, attrib, desc->proxy, desc->pending_writes, id,
- value, len))
-#endif
+ value, len, offset, bt_att_get_link_type(att)))
return;
fail:
- gatt_db_attribute_read_result(attrib, id, BT_ATT_ERROR_UNLIKELY,
- NULL, 0);
+ gatt_db_attribute_write_result(attrib, id, BT_ATT_ERROR_UNLIKELY);
}
static bool database_add_desc(struct external_service *service,
error("Unable to find device object");
goto fail;
}
-#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
- if (send_read(device, attrib, chrc->proxy, chrc->pending_reads, id, offset))
-#else
- if (send_read(device, attrib, chrc->proxy, chrc->pending_reads, id))
-#endif
+ if (send_read(device, attrib, chrc->proxy, chrc->pending_reads, id,
+ offset, bt_att_get_link_type(att)))
return;
fail:
struct external_chrc *chrc = user_data;
struct btd_device *device;
struct queue *queue;
+ DBusMessageIter iter;
if (chrc->attrib != attrib) {
error("Write callback called with incorrect attribute");
goto fail;
}
+ if (chrc->write_io) {
+ if (pipe_io_send(chrc->write_io, value, len) < 0) {
+ error("Unable to write: %s", strerror(errno));
+ goto fail;
+ }
+
+ gatt_db_attribute_write_result(attrib, id, 0);
+ return;
+ }
+
+ if (g_dbus_proxy_get_property(chrc->proxy, "WriteAcquired", &iter)) {
+ if (acquire_write(chrc, device, attrib, id, value, len,
+ bt_att_get_link_type(att)))
+ return;
+ }
+
if (!(chrc->props & BT_GATT_CHRC_PROP_WRITE_WITHOUT_RESP))
queue = chrc->pending_writes;
else
queue = NULL;
-#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
- if (send_write(device, attrib, chrc->proxy, queue, id, value, len, offset))
-#else
- if (send_write(device, attrib, chrc->proxy, queue, id, value, len))
-#endif
+ if (send_write(device, attrib, chrc->proxy, queue, id, value, len,
+ offset, bt_att_get_link_type(att)))
return;
fail:
- gatt_db_attribute_read_result(attrib, id, BT_ATT_ERROR_UNLIKELY,
- NULL, 0);
+ gatt_db_attribute_write_result(attrib, id, BT_ATT_ERROR_UNLIKELY);
}
#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
DBG("%s removed", p->name);
}
+static int profile_device_accept(struct btd_service *service)
+{
+ struct btd_profile *p = btd_service_get_profile(service);
+
+ DBG("%s accept", p->name);
+
+ return 0;
+}
+
static int profile_add(struct external_profile *profile, const char *uuid)
{
struct btd_profile *p;
p->device_probe = profile_device_probe;
p->device_remove = profile_device_remove;
+ p->accept = profile_device_accept;
p->auto_connect = true;
p->external = true;
database = new0(struct btd_gatt_database, 1);
database->adapter = btd_adapter_ref(adapter);
database->db = gatt_db_new();
+ database->records = queue_new();
database->device_states = queue_new();
database->apps = queue_new();
database->profiles = queue_new();
BT_MODE_LE,
} bt_mode_t;
+typedef enum {
+ BT_GATT_CACHE_ALWAYS,
+ BT_GATT_CACHE_YES,
+ BT_GATT_CACHE_NO,
+} bt_gatt_cache_t;
+
struct main_opts {
char *name;
uint32_t class;
- uint16_t autoto;
uint32_t pairto;
uint32_t discovto;
uint8_t privacy;
uint16_t did_version;
bt_mode_t mode;
+ bt_gatt_cache_t gatt_cache;
};
extern struct main_opts main_opts;
struct main_opts main_opts;
static GKeyFile *main_conf;
+static char *main_conf_file_path;
static enum {
MPS_OFF,
MPS_MULTIPLE,
} mps = MPS_OFF;
-static const char * const supported_options[] = {
+static const char *supported_options[] = {
"Name",
"Class",
"DiscoverableTimeout",
- "AlwaysPairable",
"PairableTimeout",
- "AutoConnectTimeout",
"DeviceID",
"ReverseServiceDiscovery",
"NameResolving",
"DebugKeys",
"ControllerMode",
"MultiProfile",
+ "FastConnectable",
"Privacy",
#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
"EnableLEPrivacy",
#endif
+ NULL
};
+static const char *policy_options[] = {
+ "ReconnectUUIDs",
+ "ReconnectAttempts",
+ "ReconnectIntervals",
+ "AutoEnable",
+ NULL
+};
+
+static const char *gatt_options[] = {
+ "Cache",
+ NULL
+};
+
+static const struct group_table {
+ const char *name;
+ const char **options;
+} valid_groups[] = {
+ { "General", supported_options },
+ { "Policy", policy_options },
+ { "GATT", gatt_options },
+ { }
+};
+
+
GKeyFile *btd_get_main_conf(void)
{
return main_conf;
main_opts.did_version = version;
}
-static void check_config(GKeyFile *config)
+static bt_gatt_cache_t parse_gatt_cache(const char *cache)
+{
+ if (!strcmp(cache, "always")) {
+ return BT_GATT_CACHE_ALWAYS;
+ } else if (!strcmp(cache, "yes")) {
+ return BT_GATT_CACHE_YES;
+ } else if (!strcmp(cache, "no")) {
+ return BT_GATT_CACHE_NO;
+ } else {
+ DBG("Invalid value for KeepCache=%s", cache);
+ return BT_GATT_CACHE_ALWAYS;
+ }
+}
+
+static void check_options(GKeyFile *config, const char *group,
+ const char **options)
{
- const char *valid_groups[] = { "General", "Policy", NULL };
char **keys;
int i;
- if (!config)
- return;
-
- keys = g_key_file_get_groups(config, NULL);
+ keys = g_key_file_get_keys(config, group, NULL, NULL);
for (i = 0; keys != NULL && keys[i] != NULL; i++) {
- const char **group;
- bool match = false;
+ bool found;
+ unsigned int j;
- for (group = valid_groups; *group; group++) {
- if (g_str_equal(keys[i], *group)) {
- match = true;
+ found = false;
+ for (j = 0; options != NULL && options[j] != NULL; j++) {
+ if (g_str_equal(keys[i], options[j])) {
+ found = true;
break;
}
}
- if (!match)
- warn("Unknown group %s in main.conf", keys[i]);
+ if (!found)
+ warn("Unknown key %s for group %s in %s",
+ keys[i], group, main_conf_file_path);
}
g_strfreev(keys);
+}
- keys = g_key_file_get_keys(config, "General", NULL, NULL);
+static void check_config(GKeyFile *config)
+{
+ char **keys;
+ int i;
+ const struct group_table *group;
+
+ if (!config)
+ return;
+
+ keys = g_key_file_get_groups(config, NULL);
for (i = 0; keys != NULL && keys[i] != NULL; i++) {
- bool found;
- unsigned int j;
+ bool match = false;
- found = false;
- for (j = 0; j < G_N_ELEMENTS(supported_options); j++) {
- if (g_str_equal(keys[i], supported_options[j])) {
- found = true;
+ for (group = valid_groups; group && group->name ; group++) {
+ if (g_str_equal(keys[i], group->name)) {
+ match = true;
break;
}
}
- if (!found)
- warn("Unknown key %s in main.conf", keys[i]);
+ if (!match)
+ warn("Unknown group %s in %s", keys[i],
+ main_conf_file_path);
}
g_strfreev(keys);
+
+ for (group = valid_groups; group && group->name; group++)
+ check_options(config, group->name, group->options);
}
static int get_mode(const char *str)
check_config(config);
- DBG("parsing main.conf");
+ DBG("parsing %s", main_conf_file_path);
val = g_key_file_get_integer(config, "General",
"DiscoverableTimeout", &err);
main_opts.pairto = val;
}
- val = g_key_file_get_integer(config, "General", "AutoConnectTimeout",
- &err);
- if (err) {
- DBG("%s", err->message);
- g_clear_error(&err);
- } else {
- DBG("auto_to=%d", val);
- main_opts.autoto = val;
- }
-
str = g_key_file_get_string(config, "General", "Privacy", &err);
if (err) {
DBG("%s", err->message);
else
main_opts.le_privacy = boolean;
#endif
+ str = g_key_file_get_string(config, "GATT", "Cache", &err);
+ if (err) {
+ g_clear_error(&err);
+ main_opts.gatt_cache = BT_GATT_CACHE_ALWAYS;
+ return;
+ }
+
+ main_opts.gatt_cache = parse_gatt_cache(str);
+
+ g_free(str);
}
static void init_defaults(void)
static char *option_debug = NULL;
static char *option_plugin = NULL;
static char *option_noplugin = NULL;
+static char *option_configfile = NULL;
static gboolean option_compat = FALSE;
static gboolean option_detach = TRUE;
static gboolean option_version = FALSE;
g_free(option_noplugin);
option_noplugin = NULL;
+
+ g_free(option_configfile);
+ option_configfile = NULL;
}
static void disconnect_dbus(void)
"Specify plugins to load", "NAME,..," },
{ "noplugin", 'P', 0, G_OPTION_ARG_STRING, &option_noplugin,
"Specify plugins not to load", "NAME,..." },
+ { "configfile", 'f', 0, G_OPTION_ARG_STRING, &option_configfile,
+ "Specify an explicit path to the config file", "FILE"},
{ "compat", 'C', 0, G_OPTION_ARG_NONE, &option_compat,
"Provide deprecated command line interfaces" },
{ "experimental", 'E', 0, G_OPTION_ARG_NONE, &option_experimental,
sd_notify(0, "STATUS=Starting up");
- main_conf = load_config(CONFIGDIR "/main.conf");
+ if (option_configfile)
+ main_conf_file_path = option_configfile;
+ else
+ main_conf_file_path = CONFIGDIR "/main.conf";
+
+ main_conf = load_config(main_conf_file_path);
parse_config(main_conf);
[General]
-# Default adaper name
+# Default adapter name
# Defaults to 'BlueZ X.YZ'
#Name = BlueZ
# Defaults to "off"
# Privacy = off
-#[Policy]
+[GATT]
+# GATT attribute cache.
+# Possible values:
+# always: Always cache attributes even for devices not paired, this is
+# recommended as it is best for interoperability, with more consistent
+# reconnection times and enables proper tracking of notifications for all
+# devices.
+# yes: Only cache attributes of paired devices.
+# no: Never cache attributes
+# Default: always
+#Cache = always
+
+[Policy]
#
# The ReconnectUUIDs defines the set of remote services that should try
# to be reconnected to in case of a link loss (link supervision
+++ /dev/null
-/*
- *
- * BlueZ - Bluetooth protocol stack for Linux
- *
- * Copyright (C) 2011 ST-Ericsson SA
- *
- * Author: Szymon Janc <szymon.janc@tieto.com> for ST-Ericsson
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
- */
-
-#include "adapter.h"
-#include "oob.h"
-
-static oob_read_cb_t local_oob_read_cb = NULL;
-
-void oob_register_cb(oob_read_cb_t cb)
-{
- local_oob_read_cb = cb;
-}
-
-#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
-void oob_read_local_data_complete(struct btd_adapter *adapter, uint8_t *hash,
- uint8_t *randomizer, void *user_data)
-#else
-void oob_read_local_data_complete(struct btd_adapter *adapter, uint8_t *hash,
- uint8_t *randomizer)
-#endif
-{
- if (local_oob_read_cb)
- local_oob_read_cb(adapter, hash, randomizer);
-}
+++ /dev/null
-/*
- *
- * BlueZ - Bluetooth protocol stack for Linux
- *
- * Copyright (C) 2011 ST-Ericsson SA
- *
- * Author: Szymon Janc <szymon.janc@tieto.com> for ST-Ericsson
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
- */
-
-typedef void (*oob_read_cb_t) (struct btd_adapter *adapter, uint8_t *hash,
- uint8_t *randomizer);
-
-void oob_register_cb(oob_read_cb_t cb);
-#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
-void oob_read_local_data_complete(struct btd_adapter *adapter, uint8_t *hash,
- uint8_t *randomizer, void *user_data);
-#else
-void oob_read_local_data_complete(struct btd_adapter *adapter, uint8_t *hash,
- uint8_t *randomizer);
-#endif
void btd_service_set_user_data(struct btd_service *service, void *user_data)
{
- btd_assert(service->state == BTD_SERVICE_STATE_UNAVAILABLE);
service->user_data = user_data;
}
struct bt_ad {
int ref_count;
+ char *name;
+ uint16_t appearance;
struct queue *service_uuids;
struct queue *manufacturer_data;
struct queue *solicit_uuids;
ad->manufacturer_data = queue_new();
ad->solicit_uuids = queue_new();
ad->service_data = queue_new();
+ ad->appearance = UINT16_MAX;
return bt_ad_ref(ad);
}
queue_destroy(ad->service_data, uuid_destroy);
+ free(ad->name);
+
free(ad);
}
return length;
}
+static size_t name_length(const char *name, size_t *pos)
+{
+ size_t len;
+
+ if (!name)
+ return 0;
+
+ len = 2 + strlen(name);
+
+ if (len > MAX_ADV_DATA_LEN - *pos)
+ len = MAX_ADV_DATA_LEN - *pos;
+
+ return len;
+}
+
static size_t calculate_length(struct bt_ad *ad)
{
size_t length = 0;
length += uuid_data_length(ad->service_data);
+ length += name_length(ad->name, &length);
+
+ length += ad->appearance != UINT16_MAX ? 4 : 0;
+
return length;
}
}
}
+static void serialize_name(const char *name, uint8_t *buf, uint8_t *pos)
+{
+ int len;
+ uint8_t type = EIR_NAME_COMPLETE;
+
+ if (!name)
+ return;
+
+ len = strlen(name);
+ if (len > MAX_ADV_DATA_LEN - (*pos + 2)) {
+ type = EIR_NAME_SHORT;
+ len = MAX_ADV_DATA_LEN - (*pos + 2);
+ }
+
+ buf[(*pos)++] = len + 1;
+ buf[(*pos)++] = type;
+
+ memcpy(buf + *pos, name, len);
+ *pos += len;
+}
+
+static void serialize_appearance(uint16_t value, uint8_t *buf, uint8_t *pos)
+{
+ if (value == UINT16_MAX)
+ return;
+
+ buf[(*pos)++] = sizeof(value) + 1;
+ buf[(*pos)++] = EIR_GAP_APPEARANCE;
+
+ bt_put_le16(value, buf + (*pos));
+ *pos += 2;
+}
+
uint8_t *bt_ad_generate(struct bt_ad *ad, size_t *length)
{
uint8_t *adv_data;
serialize_service_data(ad->service_data, adv_data, &pos);
+ serialize_name(ad->name, adv_data, &pos);
+
+ serialize_appearance(ad->appearance, adv_data, &pos);
+
return adv_data;
}
queue_remove_all(ad->service_data, NULL, NULL, uuid_destroy);
}
+
+bool bt_ad_add_name(struct bt_ad *ad, const char *name)
+{
+ if (!ad)
+ return false;
+
+ free(ad->name);
+
+ ad->name = strdup(name);
+
+ return true;
+}
+
+void bt_ad_clear_name(struct bt_ad *ad)
+{
+ if (!ad)
+ return;
+
+ free(ad->name);
+ ad->name = NULL;
+}
+
+bool bt_ad_add_appearance(struct bt_ad *ad, uint16_t appearance)
+{
+ if (!ad)
+ return false;
+
+ ad->appearance = appearance;
+
+ return true;
+}
+
+void bt_ad_clear_appearance(struct bt_ad *ad)
+{
+ if (!ad)
+ return;
+
+ ad->appearance = UINT16_MAX;
+}
bool bt_ad_remove_service_data(struct bt_ad *ad, bt_uuid_t *uuid);
void bt_ad_clear_service_data(struct bt_ad *ad);
+
+bool bt_ad_add_name(struct bt_ad *ad, const char *name);
+
+void bt_ad_clear_name(struct bt_ad *ad);
+
+bool bt_ad_add_appearance(struct bt_ad *ad, uint16_t appearance);
+
+void bt_ad_clear_appearance(struct bt_ad *ad);
return att_opcode_type_table[i].type;
}
+ if (opcode & ATT_OP_CMD_MASK)
+ return ATT_OP_TYPE_CMD;
+
return ATT_OP_TYPE_UNKNOWN;
}
/* Set in_req to false to indicate that no request is pending */
att->in_req = false;
- /* Fall through to the next case */
+ /* fall through */
#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
case ATT_OP_TYPE_CMD:
#endif
if (!change_security(att, rsp->ecode))
return false;
- util_debug(att->debug_callback, att->debug_data,
- "Retrying operation %p", op);
-
- att->pending_req = NULL;
-
-#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ /* Remove timeout_id if outstanding */
if (op->timeout_id) {
timeout_remove(op->timeout_id);
op->timeout_id = 0;
}
-#endif
+
+ util_debug(att->debug_callback, att->debug_data,
+ "Retrying operation %p", op);
+
+ att->pending_req = NULL;
/* Push operation back to request queue */
return queue_push_head(att->req_queue, op);
}
/*
- * If this was a request and no handler was registered for it, respond
- * with "Not Supported"
+ * If this was not a command and no handler was registered for it,
+ * respond with "Not Supported"
*/
- if (!found && get_op_type(opcode) == ATT_OP_TYPE_REQ)
+ if (!found && get_op_type(opcode) != ATT_OP_TYPE_CMD)
respond_not_supported(att, opcode);
bt_att_unref(att);
}
att->in_req = true;
-
- /* Fall through to the next case */
+ /* fall through */
case ATT_OP_TYPE_CMD:
case ATT_OP_TYPE_NOT:
case ATT_OP_TYPE_UNKNOWN:
case ATT_OP_TYPE_IND:
+ /* fall through */
default:
/* For all other opcodes notify the upper layer of the PDU and
* let them act on it.
if (!att || !id)
return false;
+ /* Check if disconnect is running */
+ if (!att->io) {
+ disconn = queue_find(att->disconn_list, match_disconn_id,
+ UINT_TO_PTR(id));
+ if (!disconn)
+ return false;
+
+ disconn->removed = true;
+ return true;
+ }
+
disconn = queue_remove_if(att->disconn_list, match_disconn_id,
UINT_TO_PTR(id));
if (!disconn)
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2012-2017 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
+ *
+ */
+
+#include <stdbool.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#include <ell/ell.h>
+
+#include "src/shared/btp.h"
+
+#define BTP_MTU 512
+
+struct btp_handler {
+ unsigned int id;
+ uint8_t service;
+ uint8_t opcode;
+
+ btp_cmd_func_t callback;
+ void *user_data;
+ btp_destroy_func_t destroy;
+};
+
+struct btp {
+ struct l_io *io;
+
+ struct l_queue *pending;
+ bool writer_active;
+ bool reader_active;
+
+ struct l_queue *handlers;
+ unsigned int next_handler;
+
+ uint8_t buf[BTP_MTU];
+
+ btp_disconnect_func_t disconnect_cb;
+ void *disconnect_cb_data;
+ btp_destroy_func_t disconnect_cb_data_destroy;
+};
+
+
+static struct l_io *btp_connect(const char *path)
+{
+ struct sockaddr_un addr;
+ struct l_io *io;
+ int sk;
+
+ sk = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0);
+ if (sk < 0)
+ return NULL;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1);
+
+ if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ close(sk);
+ return NULL;
+ }
+
+ io = l_io_new(sk);
+ if (!io) {
+ close(sk);
+ return NULL;
+ }
+
+ l_io_set_close_on_destroy(io, true);
+ return io;
+}
+
+static void disconnect_handler(struct l_io *io, void *user_data)
+{
+ struct btp *btp = user_data;
+
+ btp->disconnect_cb(btp, btp->disconnect_cb_data);
+}
+
+static void disconnect_handler_destroy(void *user_data)
+{
+ struct btp *btp = user_data;
+
+ if (btp->disconnect_cb_data_destroy)
+ btp->disconnect_cb_data_destroy(btp->disconnect_cb_data);
+}
+
+struct handler_match_data {
+ uint8_t service;
+ uint8_t opcode;
+};
+
+static bool handler_match(const void *a, const void *b)
+{
+ const struct btp_handler *handler = a;
+ const struct handler_match_data *match = b;
+
+ return (handler->service == match->service) &&
+ (handler->opcode == match->opcode);
+}
+
+static bool can_read_data(struct l_io *io, void *user_data)
+{
+ struct handler_match_data match;
+ struct btp *btp = user_data;
+ struct btp_handler *handler;
+ struct btp_hdr *hdr;
+ ssize_t bytes_read;
+ uint16_t data_len;
+
+ bytes_read = read(l_io_get_fd(btp->io), btp->buf, sizeof(btp->buf));
+ if (bytes_read < 0)
+ return false;
+
+ if ((size_t) bytes_read < sizeof(*hdr))
+ return false;
+
+ hdr = (void *)btp->buf;
+
+ data_len = L_LE16_TO_CPU(hdr->data_len);
+
+ if ((size_t) bytes_read < sizeof(*hdr) + data_len)
+ return false;
+
+ match.service = hdr->service;
+ match.opcode = hdr->opcode;
+
+ handler = l_queue_find(btp->handlers, handler_match, &match);
+ if (handler) {
+ handler->callback(hdr->index, hdr->data, data_len,
+ handler->user_data);
+ return false;
+ }
+
+ /* keep reader active if we sent error reply */
+ btp_send_error(btp, match.service, hdr->index, BTP_ERROR_UNKNOWN_CMD);
+ return true;
+}
+
+static void read_watch_destroy(void *user_data)
+{
+ struct btp *btp = user_data;
+
+ btp->reader_active = false;
+}
+
+static void wakeup_reader(struct btp *btp)
+{
+ if (btp->reader_active)
+ return;
+
+ btp->reader_active = l_io_set_read_handler(btp->io, can_read_data, btp,
+ read_watch_destroy);
+}
+
+struct btp *btp_new(const char *path)
+{
+ struct btp *btp;
+ struct l_io *io;
+
+ io = btp_connect(path);
+ if (!io)
+ return NULL;
+
+ btp = l_new(struct btp, 1);
+ btp->pending = l_queue_new();
+ btp->handlers = l_queue_new();
+ btp->io = io;
+ btp->next_handler = 1;
+
+ wakeup_reader(btp);
+
+ return btp;
+}
+
+struct pending_message {
+ size_t len;
+ void *data;
+ bool wakeup_read;
+};
+
+static void destroy_message(struct pending_message *msg)
+{
+ l_free(msg->data);
+ l_free(msg);
+}
+
+void btp_cleanup(struct btp *btp)
+{
+ if (!btp)
+ return;
+
+ l_io_destroy(btp->io);
+ l_queue_destroy(btp->pending, (l_queue_destroy_func_t)destroy_message);
+ l_queue_destroy(btp->handlers, (l_queue_destroy_func_t)l_free);
+ l_free(btp);
+}
+
+bool btp_set_disconnect_handler(struct btp *btp, btp_disconnect_func_t callback,
+ void *user_data, btp_destroy_func_t destroy)
+{
+ if (callback) {
+ if (!l_io_set_disconnect_handler(btp->io, disconnect_handler,
+ btp, disconnect_handler_destroy))
+ return false;
+ } else {
+ if (!l_io_set_disconnect_handler(btp->io, NULL, NULL, NULL))
+ return false;
+ }
+
+ btp->disconnect_cb = callback;
+ btp->disconnect_cb_data = user_data;
+ btp->disconnect_cb_data_destroy = destroy;
+
+ return true;
+}
+
+static bool can_write_data(struct l_io *io, void *user_data)
+{
+ struct btp *btp = user_data;
+ struct pending_message *msg;
+
+ msg = l_queue_pop_head(btp->pending);
+ if (!msg)
+ return false;
+
+ if (msg->wakeup_read)
+ wakeup_reader(btp);
+
+ if (write(l_io_get_fd(btp->io), msg->data, msg->len) < 0) {
+ l_error("Failed to send BTP message");
+ destroy_message(msg);
+ return false;
+ }
+
+ destroy_message(msg);
+
+ return !l_queue_isempty(btp->pending);
+}
+
+static void write_watch_destroy(void *user_data)
+{
+ struct btp *btp = user_data;
+
+ btp->writer_active = false;
+}
+
+static void wakeup_writer(struct btp *btp)
+{
+ if (l_queue_isempty(btp->pending))
+ return;
+
+ if (btp->writer_active)
+ return;
+
+ btp->writer_active = l_io_set_write_handler(btp->io, can_write_data,
+ btp, write_watch_destroy);
+}
+
+bool btp_send_error(struct btp *btp, uint8_t service, uint8_t index,
+ uint8_t status)
+{
+ struct btp_error rsp;
+
+ rsp.status = status;
+
+ return btp_send(btp, service, BTP_OP_ERROR, index, sizeof(rsp), &rsp);
+}
+
+bool btp_send(struct btp *btp, uint8_t service, uint8_t opcode, uint8_t index,
+ uint16_t length, const void *param)
+{
+ struct btp_hdr *hdr;
+ struct pending_message *msg;
+ size_t len;
+
+ len = sizeof(*hdr) + length;
+ hdr = l_malloc(len);
+ if (!hdr)
+ return NULL;
+
+ hdr->service = service;
+ hdr->opcode = opcode;
+ hdr->index = index;
+ hdr->data_len = L_CPU_TO_LE16(length);
+ if (length)
+ memcpy(hdr->data, param, length);
+
+ msg = l_new(struct pending_message, 1);
+ msg->len = len;
+ msg->data = hdr;
+ msg->wakeup_read = opcode < 0x80;
+
+ l_queue_push_tail(btp->pending, msg);
+ wakeup_writer(btp);
+
+ return true;
+}
+
+unsigned int btp_register(struct btp *btp, uint8_t service, uint8_t opcode,
+ btp_cmd_func_t callback, void *user_data,
+ btp_destroy_func_t destroy)
+{
+ struct btp_handler *handler;
+
+ handler = l_new(struct btp_handler, 1);
+
+ handler->id = btp->next_handler++;
+ handler->service = service;
+ handler->opcode = opcode;
+ handler->callback = callback;
+ handler->user_data = user_data;
+ handler->destroy = destroy;
+
+ l_queue_push_tail(btp->handlers, handler);
+
+ return handler->id;
+}
+
+static bool handler_match_by_id(const void *a, const void *b)
+{
+ const struct btp_handler *handler = a;
+ unsigned int id = L_PTR_TO_UINT(b);
+
+ return handler->id == id;
+}
+
+bool btp_unregister(struct btp *btp, unsigned int id)
+{
+ struct btp_handler *handler;
+
+ handler = l_queue_remove_if(btp->handlers, handler_match_by_id,
+ L_UINT_TO_PTR(id));
+ if (!handler)
+ return false;
+
+ if (handler->destroy)
+ handler->destroy(handler->user_data);
+
+ l_free(handler);
+
+ return true;
+}
+
+static bool handler_remove_by_service(void *a, void *b)
+{
+ struct btp_handler *handler = a;
+ uint8_t service = L_PTR_TO_UINT(b);
+
+ if (handler->service != service)
+ return false;
+
+ if (handler->destroy)
+ handler->destroy(handler->user_data);
+
+ l_free(handler);
+ return true;
+}
+
+void btp_unregister_service(struct btp *btp, uint8_t service)
+{
+ l_queue_foreach_remove(btp->handlers, handler_remove_by_service,
+ L_UINT_TO_PTR(service));
+}
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2012-2017 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
+ *
+ */
+
+#include <stdbool.h>
+
+#ifndef __packed
+#define __packed __attribute__((packed))
+#endif
+
+#define BTP_INDEX_NON_CONTROLLER 0xff
+
+#define BTP_ERROR_FAIL 0x01
+#define BTP_ERROR_UNKNOWN_CMD 0x02
+#define BTP_ERROR_NOT_READY 0x03
+#define BTP_ERROR_INVALID_INDEX 0x04
+
+#define BTP_CORE_SERVICE 0
+#define BTP_GAP_SERVICE 1
+#define BTP_GATT_SERVICE 2
+#define BTP_L2CAP_SERVICE 3
+#define BTP_MESH_NODE_SERVICE 4
+
+struct btp_hdr {
+ uint8_t service;
+ uint8_t opcode;
+ uint8_t index;
+ uint16_t data_len;
+ uint8_t data[0];
+} __packed;
+
+struct btp_error {
+ uint8_t status;
+} __packed;
+
+#define BTP_OP_ERROR 0x00
+
+#define BTP_OP_CORE_READ_SUPPORTED_COMMANDS 0x01
+
+#define BTP_OP_CORE_READ_SUPPORTED_SERVICES 0x02
+
+#define BTP_OP_CORE_REGISTER 0x03
+struct btp_core_register_cp {
+ uint8_t service_id;
+} __packed;
+
+#define BTP_OP_CORE_UNREGISTER 0x04
+struct btp_core_unregister_cp {
+ uint8_t service_id;
+} __packed;
+
+#define BTP_EV_CORE_READY 0x80
+
+#define BTP_OP_GAP_READ_SUPPORTED_COMMANDS 0x01
+
+#define BTP_OP_GAP_READ_CONTROLLER_INDEX_LIST 0x02
+struct btp_gap_read_index_rp {
+ uint8_t num;
+ uint8_t indexes[0];
+} __packed;
+
+#define BTP_GAP_SETTING_POWERED 0x00000001
+#define BTP_GAP_SETTING_CONNECTABLE 0x00000002
+#define BTP_GAP_SETTING_FAST_CONNECTABLE 0x00000004
+#define BTP_GAP_SETTING_DISCOVERABLE 0x00000008
+#define BTP_GAP_SETTING_BONDABLE 0x00000010
+#define BTP_GAP_SETTING_LL_SECURITY 0x00000020
+#define BTP_GAP_SETTING_SSP 0x00000040
+#define BTP_GAP_SETTING_BREDR 0x00000080
+#define BTP_GAP_SETTING_HS 0x00000100
+#define BTP_GAP_SETTING_LE 0x00000200
+#define BTP_GAP_SETTING_ADVERTISING 0x00000400
+#define BTP_GAP_SETTING_SC 0x00000800
+#define BTP_GAP_SETTING_DEBUG_KEYS 0x00001000
+#define BTP_GAP_SETTING_PRIVACY 0x00002000
+#define BTP_GAP_SETTING_CONTROLLER_CONF 0x00004000
+#define BTP_GAP_SETTING_STATIC_ADDRESS 0x00008000
+
+#define BTP_OP_GAP_READ_COTROLLER_INFO 0x03
+struct btp_gap_read_info_rp {
+ uint8_t address[6];
+ uint32_t supported_settings;
+ uint32_t current_settings;
+ uint8_t cod[3];
+ uint8_t name[249];
+ uint8_t short_name[11];
+} __packed;
+
+#define BTP_OP_GAP_RESET 0x04
+struct btp_gap_reset_rp {
+ uint32_t current_settings;
+} __packed;
+
+#define BTP_OP_GAP_SET_POWERED 0x05
+struct btp_gap_set_powered_cp {
+ uint8_t powered;
+} __packed;
+
+struct btp_gap_set_powered_rp {
+ uint32_t current_settings;
+} __packed;
+
+#define BTP_OP_GAP_SET_CONNECTABLE 0x06
+struct btp_gap_set_connectable_cp {
+ uint8_t connectable;
+} __packed;
+
+struct btp_gap_set_connectable_rp {
+ uint32_t current_settings;
+} __packed;
+
+#define BTP_OP_GAP_SET_FAST_CONNECTABLE 0x07
+struct btp_gap_set_fast_connectable_cp {
+ uint8_t fast_connectable;
+} __packed;
+
+struct btp_gap_set_fast_connectable_rp {
+ uint32_t current_settings;
+} __packed;
+
+#define BTP_OP_GAP_SET_DISCOVERABLE 0x08
+struct btp_gap_set_discoverable_cp {
+ uint8_t discoverable;
+} __packed;
+
+struct btp_gap_set_discoverable_rp {
+ uint32_t current_settings;
+} __packed;
+
+#define BTP_OP_GAP_SET_BONDABLE 0x09
+struct btp_gap_set_bondable_cp {
+ uint8_t bondable;
+} __packed;
+
+struct btp_gap_set_bondable_rp {
+ uint32_t current_settings;
+} __packed;
+
+#define BTP_OP_GAP_START_ADVERTISING 0x0a
+struct btp_gap_start_adv_cp {
+ uint8_t adv_data_len;
+ uint8_t scan_rsp_len;
+ uint8_t data[0];
+} __packed;
+
+struct btp_gap_start_adv_rp {
+ uint32_t current_settings;
+} __packed;
+
+#define BTP_OP_GAP_STOP_ADVERTISING 0x0b
+struct btp_gap_stop_adv_rp {
+ uint32_t current_settings;
+} __packed;
+
+#define BTP_GAP_DISCOVERY_FLAG_LE 0x01
+#define BTP_GAP_DISCOVERY_FLAG_BREDR 0x02
+#define BTP_GAP_DISCOVERY_FLAG_LIMITED 0x04
+#define BTP_GAP_DISCOVERY_FLAG_ACTIVE 0x08
+#define BTP_GAP_DISCOVERY_FLAG_OBSERVATION 0x10
+
+#define BTP_OP_GAP_START_DISCOVERY 0x0c
+struct btp_gap_start_discovery_cp {
+ uint8_t flags;
+} __packed;
+
+#define BTP_OP_GAP_STOP_DISCOVERY 0x0d
+
+#define BTP_GAP_ADDR_PUBLIC 0x00
+#define BTP_GAP_ADDR_RANDOM 0x01
+
+#define BTP_OP_GAP_CONNECT 0x0e
+struct btp_gap_connect_cp {
+ uint8_t address_type;
+ uint8_t address[6];
+} __packed;
+
+#define BTP_OP_GAP_DISCONNECT 0x0f
+struct btp_gap_disconnect_cp {
+ uint8_t address_type;
+ uint8_t address[6];
+} __packed;
+
+#define BTP_GAP_IOCAPA_DISPLAY_ONLY 0x00
+#define BTP_GAP_IOCAPA_DISPLAY_YESNO 0x01
+#define BTP_GAP_IOCAPA_KEYBOARD_ONLY 0x02
+#define BTP_GAP_IOCAPA_NO_INPUT_NO_OUTPUT 0x03
+#define BTP_GAP_IOCAPA_KEYBOARD_DISPLAY 0x04
+
+#define BTP_OP_GAP_SET_IO_CAPA 0x10
+struct btp_gap_set_io_capa_cp {
+ uint8_t capa;
+} __packed;
+
+#define BTP_OP_GAP_PAIR 0x11
+struct btp_gap_pair_cp {
+ uint8_t address_type;
+ uint8_t address[6];
+} __packed;
+
+#define BTP_OP_GAP_UNPAIR 0x12
+struct btp_gap_unpair_cp {
+ uint8_t address_type;
+ uint8_t address[6];
+} __packed;
+
+#define BTP_OP_GAP_PASSKEY_ENTRY_RSP 0x13
+struct btp_gap_passkey_entry_rsp_cp {
+ uint8_t address_type;
+ uint8_t address[6];
+ uint32_t passkey;
+} __packed;
+
+#define BTP_OP_GAP_PASSKEY_CONFIRM_RSP 0x14
+struct btp_gap_passkey_confirm_rsp_cp {
+ uint8_t address_type;
+ uint8_t address[6];
+ uint8_t match;
+} __packed;
+
+#define BTP_EV_GAP_NEW_SETTINGS 0x80
+struct btp_new_settings_ev {
+ uint32_t current_settings;
+} __packed;
+
+#define BTP_EV_GAP_DEVICE_FOUND_FLAG_RSSI 0x01
+#define BTP_EV_GAP_DEVICE_FOUND_FLAG_AD 0x02
+#define BTP_EV_GAP_DEVICE_FOUND_FLAG_SR 0x04
+
+#define BTP_EV_GAP_DEVICE_FOUND 0x81
+struct btp_device_found_ev {
+ uint8_t address[6];
+ uint8_t address_type;
+ int8_t rssi;
+ uint8_t flags;
+ uint16_t eir_len;
+ uint8_t eir[0];
+} __packed;
+
+#define BTP_EV_GAP_DEVICE_CONNECTED 0x82
+struct btp_gap_device_connected_ev {
+ uint8_t address_type;
+ uint8_t address[6];
+} __packed;
+
+#define BTP_EV_GAP_DEVICE_DISCONNECTED 0x83
+struct btp_gap_device_disconnected_ev {
+ uint8_t address_type;
+ uint8_t address[6];
+} __packed;
+
+#define BTP_EV_GAP_PASSKEY_DISPLAY 0x84
+struct btp_gap_passkey_display_ev {
+ uint8_t address_type;
+ uint8_t address[6];
+ uint32_t passkey;
+} __packed;
+
+#define BTP_EV_GAP_PASSKEY_REQUEST 0x85
+struct btp_gap_passkey_req_ev {
+ uint8_t address_type;
+ uint8_t address[6];
+} __packed;
+
+#define BTP_EV_GAP_PASSKEY_CONFIRM 0x86
+struct btp_gap_passkey_confirm_ev {
+ uint8_t address_type;
+ uint8_t address[6];
+ uint32_t passkey;
+} __packed;
+
+#define BTP_EV_GAP_IDENTITY_RESOLVED 0x87
+struct btp_gap_identity_resolved_ev {
+ uint8_t address_type;
+ uint8_t address[6];
+ uint8_t identity_address_type;
+ uint8_t identity_address[6];
+} __packed;
+
+struct btp;
+
+typedef void (*btp_destroy_func_t)(void *user_data);
+typedef void (*btp_disconnect_func_t)(struct btp *btp, void *user_data);
+typedef void (*btp_cmd_func_t)(uint8_t index, const void *param,
+ uint16_t length, void *user_data);
+
+struct btp *btp_new(const char *path);
+void btp_cleanup(struct btp *btp);
+
+bool btp_set_disconnect_handler(struct btp *btp, btp_disconnect_func_t callback,
+ void *user_data, btp_destroy_func_t destroy);
+
+bool btp_send_error(struct btp *btp, uint8_t service, uint8_t index,
+ uint8_t status);
+bool btp_send(struct btp *btp, uint8_t service, uint8_t opcode, uint8_t index,
+ uint16_t length, const void *param);
+
+unsigned int btp_register(struct btp *btp, uint8_t service, uint8_t opcode,
+ btp_cmd_func_t callback, void *user_data,
+ btp_destroy_func_t destroy);
+bool btp_unregister(struct btp *btp, unsigned int id);
+void btp_unregister_service(struct btp *btp, uint8_t service);
#define GATT_SVC_UUID 0x1801
#define SVC_CHNGD_UUID 0x2a05
+struct ready_cb {
+ bt_gatt_client_callback_t callback;
+ bt_gatt_client_destroy_func_t destroy;
+ void *data;
+};
+
struct bt_gatt_client {
struct bt_att *att;
int ref_count;
struct bt_gatt_client *parent;
struct queue *clones;
- bt_gatt_client_callback_t ready_callback;
- bt_gatt_client_destroy_func_t ready_destroy;
- void *ready_data;
+ struct queue *ready_cbs;
bt_gatt_client_service_changed_callback_t svc_chngd_callback;
bt_gatt_client_destroy_func_t svc_chngd_destroy;
{
struct request *req;
+ if (!client->att)
+ return NULL;
+
req = new0(struct request, 1);
if (client->next_request_id < 1)
static void discovery_op_complete(struct discovery_op *op, bool success,
uint8_t err)
{
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
const struct queue_entry *svc;
/*
gatt_db_remove_service(op->client->db, attr);
}
+
/* Reset remaining range */
if (success) {
-#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
util_debug(op->client->debug_callback, op->client->debug_data,
"op->start : %u, op->end : %u, op->last : %u",
op->start, op->end, op->last);
gatt_db_clear_range(op->client->db,
op->last, op->end);
}
-#else
- if (op->last != UINT16_MAX)
- gatt_db_clear_range(op->client->db, op->last + 1,
- UINT16_MAX);
-#endif
} else {
-#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
util_debug(op->client->debug_callback, op->client->debug_data,
"Fail to discover service. Clear DB [%d]", err);
-#endif
gatt_db_clear(op->client->db);
}
op->success = success;
op->complete_func(op, success, err);
+#else
+ /* Reset remaining range */
+ if (op->last != UINT16_MAX)
+ gatt_db_clear_range(op->client->db, op->last + 1, UINT16_MAX);
+
+ op->success = success;
+ op->complete_func(op, success, err);
+#endif
}
static void discovery_load_services(struct gatt_db_attribute *attr,
op->end = end;
#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
op->last = start;
+#else
+ op->last = gatt_db_isempty(client->db) ? 0 : UINT16_MAX;
#endif
/* Load existing services as pending */
if (__sync_sub_and_fetch(&op->ref_count, 1))
return;
- if (!op->success)
+ if (!op->success && op->failure_func)
op->failure_func(op);
discovery_op_free(op);
return;
if (!discover_descs(op, &discovering))
- goto failed;
+ goto failed;
if (discovering)
return;
/* Done with the current service */
gatt_db_service_set_active(op->cur_svc, true);
+
goto done;
failed:
success = false;
goto done;
}
+ /* Database has changed adjust last handle */
+ op->last = end;
}
/* Update pending list */
util_debug(client->debug_callback, client->debug_data,
"Failed to start included services discovery");
discovery_op_unref(op);
+ success = false;
done:
discovery_op_complete(op, success, att_ecode);
"Primary service discovery failed."
" ATT ECODE: 0x%02x", att_ecode);
/* Reset error in case of not found */
-#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
- if (att_ecode == BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND) {
-#else
- if (BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND) {
-#endif
+ switch (att_ecode) {
+ case BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND:
success = true;
att_ecode = 0;
+ goto secondary;
+ default:
+ goto done;
}
- goto secondary;
}
if (!result || !bt_gatt_iter_init(&iter, result)) {
success = false;
goto done;
}
+ /* Database has changed adjust last handle */
+ op->last = end;
}
/* Update pending list */
discovery_op_complete(op, success, att_ecode);
}
+static void ready_destroy(void *data)
+{
+ struct ready_cb *ready = data;
+
+ if (ready->destroy)
+ ready->destroy(ready->data);
+
+ free(ready);
+}
+
static void notify_client_ready(struct bt_gatt_client *client, bool success,
uint8_t att_ecode)
{
const struct queue_entry *entry;
- if (!client->ready_callback || client->ready)
+ if (client->ready)
return;
bt_gatt_client_ref(client);
client->ready = success;
- client->ready_callback(success, att_ecode, client->ready_data);
+
+ for (entry = queue_get_entries(client->ready_cbs); entry;
+ entry = entry->next) {
+ struct ready_cb *ready = entry->data;
+
+ ready->callback(success, att_ecode, ready->data);
+ }
+
+ queue_remove_all(client->ready_cbs, NULL, NULL, ready_destroy);
/* Notify clones */
for (entry = queue_get_entries(client->clones); entry;
uint16_t length, void *user_data)
{
struct notify_data *notify_data = user_data;
- uint16_t att_ecode;
+ uint8_t att_ecode;
assert(notify_data->chrc->ccc_write_id);
#endif
}
-static void init_fail(struct discovery_op *op)
-{
- struct bt_gatt_client *client = op->client;
-
-#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
- util_debug(client->debug_callback, client->debug_data,
- "GATT client init is failed");
-#endif
-
- gatt_db_clear(client->db);
-}
-
static bool gatt_client_init(struct bt_gatt_client *client, uint16_t mtu)
{
struct discovery_op *op;
if (client->in_init || client->ready)
return false;
- op = discovery_op_create(client, 0x0001, 0xffff, init_complete,
- init_fail);
+ op = discovery_op_create(client, 0x0001, 0xffff, init_complete, NULL);
if (!op)
return false;
queue_destroy(client->notify_list, notify_data_cleanup);
- if (client->ready_destroy)
- client->ready_destroy(client->ready_data);
+ queue_destroy(client->ready_cbs, ready_destroy);
if (client->debug_destroy)
client->debug_destroy(client->debug_data);
goto fail;
client->clones = queue_new();
+ client->ready_cbs = queue_new();
client->long_write_queue = queue_new();
client->svc_chngd_queue = queue_new();
client->notify_list = queue_new();
return (client && client->ready);
}
-bool bt_gatt_client_set_ready_handler(struct bt_gatt_client *client,
+unsigned int bt_gatt_client_ready_register(struct bt_gatt_client *client,
bt_gatt_client_callback_t callback,
void *user_data,
bt_gatt_client_destroy_func_t destroy)
{
+ struct ready_cb *ready;
+
if (!client)
- return false;
+ return 0;
- if (client->ready_destroy)
- client->ready_destroy(client->ready_data);
+ ready = new0(struct ready_cb, 1);
+ ready->callback = callback;
+ ready->destroy = destroy;
+ ready->data = user_data;
- client->ready_callback = callback;
- client->ready_destroy = destroy;
- client->ready_data = user_data;
+ queue_push_tail(client->ready_cbs, ready);
- return true;
+ return PTR_TO_UINT(ready);
+}
+
+bool bt_gatt_client_ready_unregister(struct bt_gatt_client *client,
+ unsigned int id)
+{
+ struct ready_cb *ready = UINT_TO_PTR(id);
+
+ if (queue_remove(client->ready_cbs, ready)) {
+ ready_destroy(ready);
+ return true;
+ }
+
+ return false;
}
bool bt_gatt_client_set_service_changed(struct bt_gatt_client *client,
void *user_data);
bool bt_gatt_client_is_ready(struct bt_gatt_client *client);
-bool bt_gatt_client_set_ready_handler(struct bt_gatt_client *client,
+unsigned int bt_gatt_client_ready_register(struct bt_gatt_client *client,
bt_gatt_client_callback_t callback,
void *user_data,
bt_gatt_client_destroy_func_t destroy);
+bool bt_gatt_client_ready_unregister(struct bt_gatt_client *client,
+ unsigned int id);
bool bt_gatt_client_set_service_changed(struct bt_gatt_client *client,
bt_gatt_client_service_changed_callback_t callback,
void *user_data,
bool gatt_db_clear(struct gatt_db *db)
{
- if (!db)
- return false;
-
- queue_remove_all(db->services, NULL, NULL, gatt_db_service_destroy);
-
- db->next_handle = 0;
-
- return true;
+ return gatt_db_clear_range(db, 1, UINT16_MAX);
}
static void gatt_db_service_get_handles(const struct gatt_db_service *service,
if (!db || start_handle > end_handle)
return false;
+ /* Check if it is a full clear */
+ if (start_handle == 1 && end_handle == UINT16_MAX) {
+ queue_remove_all(db->services, NULL, NULL,
+ gatt_db_service_destroy);
+ goto done;
+ }
+
range.start = start_handle;
range.end = end_handle;
queue_remove_all(db->services, match_range, &range,
gatt_db_service_destroy);
+done:
+ if (gatt_db_isempty(db))
+ db->next_handle = 0;
+
return true;
}
continue;
/* TODO: fix for read-callback based attributes */
- if (search_data->value && memcmp(attribute->value,
- search_data->value,
- search_data->value_len))
- continue;
+ if (search_data->value) {
+ if (search_data->value_len != attribute->value_len)
+ continue;
+
+ if (memcmp(attribute->value, search_data->value,
+ search_data->value_len)) {
+ continue;
+ }
+ }
search_data->num_of_res++;
search_data->func(attribute, search_data->user_data);
return NULL;
}
#endif
+
+void *gatt_db_attribute_get_user_data(struct gatt_db_attribute *attrib)
+{
+ if (!attrib)
+ return NULL;
+
+ return attrib->user_data;
+}
const bdaddr_t *get_ccc_unicast_address(const struct gatt_db_attribute *ccc);
#endif
+void *gatt_db_attribute_get_user_data(struct gatt_db_attribute *attrib);
write_complete_cb, op))
return;
- if (op)
- async_write_op_destroy(op);
+ async_write_op_destroy(op);
ecode = BT_ATT_ERROR_UNLIKELY;
return bt_gatt_server_ref(server);
}
+uint16_t bt_gatt_server_get_mtu(struct bt_gatt_server *server)
+{
+ if (!server || !server->att)
+ return 0;
+
+ return bt_att_get_mtu(server->att);
+}
+
struct bt_gatt_server *bt_gatt_server_ref(struct bt_gatt_server *server)
{
if (!server)
struct bt_gatt_server *bt_gatt_server_new(struct gatt_db *db,
struct bt_att *att, uint16_t mtu);
+uint16_t bt_gatt_server_get_mtu(struct bt_gatt_server *server);
struct bt_gatt_server *bt_gatt_server_ref(struct bt_gatt_server *server);
void bt_gatt_server_unref(struct bt_gatt_server *server);
if (data->destroy)
data->destroy(data->user_data);
+
+ free(data);
}
static void timeout_callback(int fd, uint32_t events, void *user_data)
itimer.it_interval.tv_sec = 0;
itimer.it_interval.tv_nsec = 0;
itimer.it_value.tv_sec = sec;
- itimer.it_value.tv_nsec = (msec - (sec * 1000)) * 1000;
+ itimer.it_value.tv_nsec = (msec - (sec * 1000)) * 1000 * 1000;
return timerfd_settime(fd, 0, &itimer, NULL);
}
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2017 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>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <signal.h>
+#include <sys/signalfd.h>
+#include <wordexp.h>
+#include <getopt.h>
+
+#include <readline/readline.h>
+#include <readline/history.h>
+#include <glib.h>
+
+#include "src/shared/io.h"
+#include "src/shared/util.h"
+#include "src/shared/queue.h"
+#include "src/shared/shell.h"
+
+#define CMD_LENGTH 48
+#define print_text(color, fmt, args...) \
+ printf(color fmt COLOR_OFF "\n", ## args)
+#define print_menu(cmd, args, desc) \
+ printf(COLOR_HIGHLIGHT "%s %-*s " COLOR_OFF "%s\n", \
+ cmd, (int)(CMD_LENGTH - strlen(cmd)), args, desc)
+#define print_submenu(cmd, desc) \
+ printf(COLOR_BLUE "%s %-*s " COLOR_OFF "%s\n", \
+ cmd, (int)(CMD_LENGTH - strlen(cmd)), "", desc)
+
+static GMainLoop *main_loop;
+
+static struct {
+ struct io *input;
+
+ bool saved_prompt;
+ bt_shell_prompt_input_func saved_func;
+ void *saved_user_data;
+
+ const struct bt_shell_menu *menu;
+ const struct bt_shell_menu *main;
+ struct queue *submenus;
+} data;
+
+static void shell_print_menu(void);
+
+static void cmd_version(int argc, char *argv[])
+{
+ bt_shell_printf("Version %s\n", VERSION);
+}
+
+static void cmd_quit(int argc, char *argv[])
+{
+ g_main_loop_quit(main_loop);
+}
+
+static void cmd_help(int argc, char *argv[])
+{
+ shell_print_menu();
+}
+
+static const struct bt_shell_menu *find_menu(const char *name)
+{
+ const struct queue_entry *entry;
+
+ for (entry = queue_get_entries(data.submenus); entry;
+ entry = entry->next) {
+ struct bt_shell_menu *menu = entry->data;
+
+ if (!strcmp(menu->name, name))
+ return menu;
+ }
+
+ return NULL;
+}
+
+static char *menu_generator(const char *text, int state)
+{
+ static unsigned int index, len;
+ static struct queue_entry *entry;
+
+ if (!state) {
+ index = 0;
+ len = strlen(text);
+ entry = (void *) queue_get_entries(data.submenus);
+ }
+
+ for (; entry; entry = entry->next) {
+ struct bt_shell_menu *menu = entry->data;
+
+ index++;
+
+ if (!strncmp(menu->name, text, len)) {
+ entry = entry->next;
+ return strdup(menu->name);
+ }
+ }
+
+ return NULL;
+}
+
+static void cmd_menu(int argc, char *argv[])
+{
+ const struct bt_shell_menu *menu;
+
+ if (argc < 2 || !strlen(argv[1])) {
+ bt_shell_printf("Missing name argument\n");
+ return;
+ }
+
+ menu = find_menu(argv[1]);
+ if (!menu) {
+ bt_shell_printf("Unable find menu with name: %s\n", argv[1]);
+ return;
+ }
+
+ bt_shell_set_menu(menu);
+
+ shell_print_menu();
+}
+
+static bool cmd_menu_exists(const struct bt_shell_menu *menu)
+{
+ /* Skip menu command if not on main menu or if there are no
+ * submenus.
+ */
+ if (menu != data.main || queue_isempty(data.submenus))
+ return false;
+
+ return true;
+}
+
+static void cmd_back(int argc, char *argv[])
+{
+ if (data.menu == data.main) {
+ bt_shell_printf("Already on main menu\n");
+ return;
+ }
+
+ bt_shell_set_menu(data.main);
+
+ shell_print_menu();
+}
+
+static bool cmd_back_exists(const struct bt_shell_menu *menu)
+{
+ /* Skip back command if on main menu */
+ if (menu == data.main)
+ return false;
+
+ return true;
+}
+
+static const struct bt_shell_menu_entry default_menu[] = {
+ { "back", NULL, cmd_back, "Return to main menu", NULL,
+ NULL, cmd_back_exists },
+ { "menu", "<name>", cmd_menu, "Select submenu",
+ menu_generator, NULL,
+ cmd_menu_exists},
+ { "version", NULL, cmd_version, "Display version" },
+ { "quit", NULL, cmd_quit, "Quit program" },
+ { "exit", NULL, cmd_quit, "Quit program" },
+ { "help", NULL, cmd_help,
+ "Display help about this program" },
+ { }
+};
+
+static void shell_print_menu(void)
+{
+ const struct bt_shell_menu_entry *entry;
+ const struct queue_entry *submenu;
+
+ if (!data.menu)
+ return;
+
+ print_text(COLOR_HIGHLIGHT, "Menu %s:", data.menu->name);
+ print_text(COLOR_HIGHLIGHT, "Available commands:");
+ print_text(COLOR_HIGHLIGHT, "-------------------");
+
+ if (data.menu == data.main) {
+ for (submenu = queue_get_entries(data.submenus); submenu;
+ submenu = submenu->next) {
+ struct bt_shell_menu *menu = submenu->data;
+
+ print_submenu(menu->name, menu->desc ? menu->desc :
+ "Submenu");
+ }
+ }
+
+ for (entry = data.menu->entries; entry->cmd; entry++) {
+ print_menu(entry->cmd, entry->arg ? : "", entry->desc ? : "");
+ }
+
+ for (entry = default_menu; entry->cmd; entry++) {
+ if (entry->exists && !entry->exists(data.menu))
+ continue;
+
+ print_menu(entry->cmd, entry->arg ? : "", entry->desc ? : "");
+ }
+}
+
+static int parse_args(char *arg, wordexp_t *w, char *del, int flags)
+{
+ char *str;
+
+ str = g_strdelimit(arg, del, '"');
+
+ if (wordexp(str, w, flags)) {
+ g_free(str);
+ return -EINVAL;
+ }
+
+ /* If argument ends with ,,, set we_offs bypass strict checks */
+ if (w->we_wordc && g_str_has_suffix(w->we_wordv[w->we_wordc -1], "..."))
+ w->we_offs = 1;
+
+ g_free(str);
+
+ return 0;
+}
+
+static int cmd_exec(const struct bt_shell_menu_entry *entry,
+ int argc, char *argv[])
+{
+ wordexp_t w;
+ size_t len;
+ char *man, *opt;
+ int flags = WRDE_NOCMD;
+
+ if (!entry->arg || entry->arg[0] == '\0') {
+ if (argc > 1) {
+ print_text(COLOR_HIGHLIGHT, "Too many arguments");
+ return -EINVAL;
+ }
+ goto exec;
+ }
+
+ /* Find last mandatory arguments */
+ man = strrchr(entry->arg, '>');
+ if (!man) {
+ opt = strdup(entry->arg);
+ goto optional;
+ }
+
+ len = man - entry->arg;
+ man = strndup(entry->arg, len + 1);
+
+ if (parse_args(man, &w, "<>", flags) < 0) {
+ print_text(COLOR_HIGHLIGHT,
+ "Unable to parse mandatory command arguments");
+ return -EINVAL;
+ }
+
+ /* Check if there are enough arguments */
+ if ((unsigned) argc - 1 < w.we_wordc && !w.we_offs) {
+ print_text(COLOR_HIGHLIGHT, "Missing %s argument",
+ w.we_wordv[argc - 1]);
+ goto fail;
+ }
+
+ flags |= WRDE_APPEND;
+ opt = strdup(entry->arg + len + 1);
+
+optional:
+ if (parse_args(opt, &w, "[]", flags) < 0) {
+ print_text(COLOR_HIGHLIGHT,
+ "Unable to parse optional command arguments");
+ return -EINVAL;
+ }
+
+ /* Check if there are too many arguments */
+ if ((unsigned) argc - 1 > w.we_wordc && !w.we_offs) {
+ print_text(COLOR_HIGHLIGHT, "Too many arguments: %d > %zu",
+ argc - 1, w.we_wordc);
+ goto fail;
+ }
+
+ wordfree(&w);
+
+exec:
+ if (entry->func)
+ entry->func(argc, argv);
+
+ return 0;
+
+fail:
+ wordfree(&w);
+ return -EINVAL;
+}
+
+static int menu_exec(const struct bt_shell_menu_entry *entry,
+ int argc, char *argv[])
+{
+ for (; entry->cmd; entry++) {
+ if (strcmp(argv[0], entry->cmd))
+ continue;
+
+ /* Skip menu command if not on main menu */
+ if (data.menu != data.main && !strcmp(entry->cmd, "menu"))
+ continue;
+
+ /* Skip back command if on main menu */
+ if (data.menu == data.main && !strcmp(entry->cmd, "back"))
+ continue;
+
+ return cmd_exec(entry, argc, argv);
+ }
+
+ return -ENOENT;
+}
+
+static void shell_exec(int argc, char *argv[])
+{
+ if (!data.menu || !argv[0])
+ return;
+
+ if (menu_exec(default_menu, argc, argv) == -ENOENT) {
+ if (menu_exec(data.menu->entries, argc, argv) == -ENOENT)
+ print_text(COLOR_HIGHLIGHT, "Invalid command");
+ }
+}
+
+void bt_shell_printf(const char *fmt, ...)
+{
+ va_list args;
+ bool save_input;
+ char *saved_line;
+ int saved_point;
+
+ save_input = !RL_ISSTATE(RL_STATE_DONE);
+
+ if (save_input) {
+ saved_point = rl_point;
+ saved_line = rl_copy_text(0, rl_end);
+ if (!data.saved_prompt) {
+ rl_save_prompt();
+ rl_replace_line("", 0);
+ rl_redisplay();
+ }
+ }
+
+ va_start(args, fmt);
+ vprintf(fmt, args);
+ va_end(args);
+
+ if (save_input) {
+ if (!data.saved_prompt)
+ rl_restore_prompt();
+ rl_replace_line(saved_line, 0);
+ rl_point = saved_point;
+ rl_forced_update_display();
+ free(saved_line);
+ }
+}
+
+void bt_shell_hexdump(const unsigned char *buf, size_t len)
+{
+ static const char hexdigits[] = "0123456789abcdef";
+ char str[68];
+ size_t i;
+
+ if (!len)
+ return;
+
+ str[0] = ' ';
+
+ for (i = 0; i < len; i++) {
+ str[((i % 16) * 3) + 1] = ' ';
+ str[((i % 16) * 3) + 2] = hexdigits[buf[i] >> 4];
+ str[((i % 16) * 3) + 3] = hexdigits[buf[i] & 0xf];
+ str[(i % 16) + 51] = isprint(buf[i]) ? buf[i] : '.';
+
+ if ((i + 1) % 16 == 0) {
+ str[49] = ' ';
+ str[50] = ' ';
+ str[67] = '\0';
+ bt_shell_printf("%s\n", str);
+ str[0] = ' ';
+ }
+ }
+
+ if (i % 16 > 0) {
+ size_t j;
+ for (j = (i % 16); j < 16; j++) {
+ str[(j * 3) + 1] = ' ';
+ str[(j * 3) + 2] = ' ';
+ str[(j * 3) + 3] = ' ';
+ str[j + 51] = ' ';
+ }
+ str[49] = ' ';
+ str[50] = ' ';
+ str[67] = '\0';
+ bt_shell_printf("%s\n", str);
+ }
+}
+
+void bt_shell_prompt_input(const char *label, const char *msg,
+ bt_shell_prompt_input_func func, void *user_data)
+{
+ /* Normal use should not prompt for user input to the value a second
+ * time before it releases the prompt, but we take a safe action. */
+ if (data.saved_prompt)
+ return;
+
+ rl_save_prompt();
+ rl_message(COLOR_RED "[%s]" COLOR_OFF " %s ", label, msg);
+
+ data.saved_prompt = true;
+ data.saved_func = func;
+ data.saved_user_data = user_data;
+}
+
+int bt_shell_release_prompt(const char *input)
+{
+ bt_shell_prompt_input_func func;
+ void *user_data;
+
+ if (!data.saved_prompt)
+ return -1;
+
+ data.saved_prompt = false;
+
+ rl_restore_prompt();
+
+ func = data.saved_func;
+ user_data = data.saved_user_data;
+
+ data.saved_func = NULL;
+ data.saved_user_data = NULL;
+
+ func(input, user_data);
+
+ return 0;
+}
+
+static void rl_handler(char *input)
+{
+ wordexp_t w;
+
+ if (!input) {
+ rl_insert_text("quit");
+ rl_redisplay();
+ rl_crlf();
+ g_main_loop_quit(main_loop);
+ return;
+ }
+
+ if (!strlen(input))
+ goto done;
+
+ if (!bt_shell_release_prompt(input))
+ goto done;
+
+ if (history_search(input, -1))
+ add_history(input);
+
+ if (wordexp(input, &w, WRDE_NOCMD))
+ goto done;
+
+ if (w.we_wordc == 0) {
+ wordfree(&w);
+ goto done;
+ }
+
+ shell_exec(w.we_wordc, w.we_wordv);
+ wordfree(&w);
+done:
+ free(input);
+}
+
+static char *find_cmd(const char *text,
+ const struct bt_shell_menu_entry *entry, int *index)
+{
+ const struct bt_shell_menu_entry *tmp;
+ int len;
+
+ len = strlen(text);
+
+ while ((tmp = &entry[*index])) {
+ (*index)++;
+
+ if (!tmp->cmd)
+ break;
+
+ if (tmp->exists && !tmp->exists(data.menu))
+ continue;
+
+ if (!strncmp(tmp->cmd, text, len))
+ return strdup(tmp->cmd);
+ }
+
+ return NULL;
+}
+
+static char *cmd_generator(const char *text, int state)
+{
+ static int index;
+ static bool default_menu_enabled;
+ char *cmd;
+
+ if (!state) {
+ index = 0;
+ default_menu_enabled = true;
+ }
+
+ if (default_menu_enabled) {
+ cmd = find_cmd(text, default_menu, &index);
+ if (cmd) {
+ return cmd;
+ } else {
+ index = 0;
+ default_menu_enabled = false;
+ }
+ }
+
+ return find_cmd(text, data.menu->entries, &index);
+}
+
+static char **menu_completion(const struct bt_shell_menu_entry *entry,
+ const char *text, char *input_cmd)
+{
+ char **matches = NULL;
+
+ for (; entry->cmd; entry++) {
+ if (strcmp(entry->cmd, input_cmd))
+ continue;
+
+ if (!entry->gen) {
+ if (text[0] == '\0')
+ bt_shell_printf("Usage: %s %s\n", entry->cmd,
+ entry->arg ? entry->arg : "");
+ break;
+ }
+
+ rl_completion_display_matches_hook = entry->disp;
+ matches = rl_completion_matches(text, entry->gen);
+ break;
+ }
+
+ return matches;
+}
+
+static char **shell_completion(const char *text, int start, int end)
+{
+ char **matches = NULL;
+
+ if (!data.menu)
+ return NULL;
+
+ if (start > 0) {
+ wordexp_t w;
+
+ if (wordexp(rl_line_buffer, &w, WRDE_NOCMD))
+ return NULL;
+
+ matches = menu_completion(default_menu, text, w.we_wordv[0]);
+ if (!matches)
+ matches = menu_completion(data.menu->entries, text,
+ w.we_wordv[0]);
+
+ wordfree(&w);
+ } else {
+ rl_completion_display_matches_hook = NULL;
+ matches = rl_completion_matches(text, cmd_generator);
+ }
+
+ if (!matches)
+ rl_attempted_completion_over = 1;
+
+ return matches;
+}
+
+static bool io_hup(struct io *io, void *user_data)
+{
+ g_main_loop_quit(main_loop);
+
+ return false;
+}
+
+static bool signal_read(struct io *io, void *user_data)
+{
+ static bool terminated = false;
+ struct signalfd_siginfo si;
+ ssize_t result;
+ int fd;
+
+ fd = io_get_fd(io);
+
+ result = read(fd, &si, sizeof(si));
+ if (result != sizeof(si))
+ return false;
+
+ switch (si.ssi_signo) {
+ case SIGINT:
+ if (data.input) {
+ rl_replace_line("", 0);
+ rl_crlf();
+ rl_on_new_line();
+ rl_redisplay();
+ break;
+ }
+
+ /*
+ * If input was not yet setup up that means signal was received
+ * while daemon was not yet running. Since user is not able
+ * to terminate client by CTRL-D or typing exit treat this as
+ * exit and fall through.
+ */
+
+ /* fall through */
+ case SIGTERM:
+ if (!terminated) {
+ rl_replace_line("", 0);
+ rl_crlf();
+ g_main_loop_quit(main_loop);
+ }
+
+ terminated = true;
+ break;
+ }
+
+ return false;
+}
+
+static struct io *setup_signalfd(void)
+{
+ struct io *io;
+ sigset_t mask;
+ int fd;
+
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGINT);
+ sigaddset(&mask, SIGTERM);
+
+ if (sigprocmask(SIG_BLOCK, &mask, NULL) < 0) {
+ perror("Failed to set signal mask");
+ return 0;
+ }
+
+ fd = signalfd(-1, &mask, 0);
+ if (fd < 0) {
+ perror("Failed to create signal descriptor");
+ return 0;
+ }
+
+ io = io_new(fd);
+
+ io_set_close_on_destroy(io, true);
+ io_set_read_handler(io, signal_read, NULL, NULL);
+ io_set_disconnect_handler(io, io_hup, NULL, NULL);
+
+ return io;
+}
+
+static void rl_init(void)
+{
+ setlinebuf(stdout);
+ rl_attempted_completion_function = shell_completion;
+
+ rl_erase_empty_line = 1;
+ rl_callback_handler_install(NULL, rl_handler);
+}
+
+static const struct option main_options[] = {
+ { "version", no_argument, 0, 'v' },
+ { "help", no_argument, 0, 'h' },
+};
+
+static void usage(int argc, char **argv, const struct bt_shell_opt *opt)
+{
+ unsigned int i;
+
+ printf("%s ver %s\n", argv[0], VERSION);
+ printf("Usage:\n"
+ "\t%s [options]\n", argv[0]);
+
+ printf("Options:\n");
+
+ for (i = 0; opt && opt->options[i].name; i++)
+ printf("\t--%s \t%s\n", opt->options[i].name, opt->help[i]);
+
+ printf("\t--version \tDisplay version\n"
+ "\t--help \t\tDisplay help\n");
+}
+
+void bt_shell_init(int argc, char **argv, const struct bt_shell_opt *opt)
+{
+ int c, index = 0;
+ struct option options[256];
+ char optstr[256];
+ size_t offset;
+
+ offset = sizeof(main_options) / sizeof(struct option);
+
+ memcpy(options, main_options, sizeof(struct option) * offset);
+
+ if (opt) {
+ memcpy(options + offset, opt->options,
+ sizeof(struct option) * opt->optno);
+ snprintf(optstr, sizeof(optstr), "+hv%s", opt->optstr);
+ } else
+ snprintf(optstr, sizeof(optstr), "+hv");
+
+ while ((c = getopt_long(argc, argv, optstr, options, &index)) != -1) {
+ switch (c) {
+ case 'v':
+ printf("%s: %s\n", argv[0], VERSION);
+ exit(EXIT_SUCCESS);
+ return;
+ case 'h':
+ usage(argc, argv, opt);
+ exit(EXIT_SUCCESS);
+ return;
+ default:
+ if (c != opt->options[index - offset].val) {
+ usage(argc, argv, opt);
+ exit(EXIT_SUCCESS);
+ return;
+ }
+
+ *opt->optarg[index - offset] = optarg;
+ }
+ }
+
+ main_loop = g_main_loop_new(NULL, FALSE);
+
+ rl_init();
+}
+
+static void rl_cleanup(void)
+{
+ rl_message("");
+ rl_callback_handler_remove();
+}
+
+void bt_shell_run(void)
+{
+ struct io *signal;
+
+ signal = setup_signalfd();
+
+ g_main_loop_run(main_loop);
+
+ bt_shell_release_prompt("");
+ bt_shell_detach();
+
+ io_destroy(signal);
+
+ g_main_loop_unref(main_loop);
+ main_loop = NULL;
+
+ rl_cleanup();
+}
+
+bool bt_shell_set_menu(const struct bt_shell_menu *menu)
+{
+ if (!menu)
+ return false;
+
+ data.menu = menu;
+
+ if (!data.main)
+ data.main = menu;
+
+ return true;
+}
+
+bool bt_shell_add_submenu(const struct bt_shell_menu *menu)
+{
+ if (!menu)
+ return false;
+
+ if (!data.submenus)
+ data.submenus = queue_new();
+
+ queue_push_tail(data.submenus, (void *) menu);
+
+ return true;
+}
+
+void bt_shell_set_prompt(const char *string)
+{
+ if (!main_loop)
+ return;
+
+ rl_set_prompt(string);
+ printf("\r");
+ rl_on_new_line();
+ rl_redisplay();
+}
+
+static bool input_read(struct io *io, void *user_data)
+{
+ rl_callback_read_char();
+ return true;
+}
+
+bool bt_shell_attach(int fd)
+{
+ struct io *io;
+
+ /* TODO: Allow more than one input? */
+ if (data.input)
+ return false;
+
+ io = io_new(fd);
+
+ io_set_read_handler(io, input_read, NULL, NULL);
+ io_set_disconnect_handler(io, io_hup, NULL, NULL);
+
+ data.input = io;
+
+ return true;
+}
+
+bool bt_shell_detach(void)
+{
+ if (!data.input)
+ return false;
+
+ io_destroy(data.input);
+ data.input = NULL;
+
+ return true;
+}
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2017 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
+ *
+ */
+#include <getopt.h>
+
+#define COLOR_OFF "\x1B[0m"
+#define COLOR_RED "\x1B[0;91m"
+#define COLOR_GREEN "\x1B[0;92m"
+#define COLOR_YELLOW "\x1B[0;93m"
+#define COLOR_BLUE "\x1B[0;94m"
+#define COLOR_BOLDGRAY "\x1B[1;30m"
+#define COLOR_BOLDWHITE "\x1B[1;37m"
+#define COLOR_HIGHLIGHT "\x1B[1;39m"
+
+struct bt_shell_menu;
+
+typedef void (*bt_shell_menu_cb_t)(int argc, char *argv[]);
+typedef char * (*bt_shell_menu_gen_t)(const char *text, int state);
+typedef void (*bt_shell_menu_disp_t) (char **matches, int num_matches,
+ int max_length);
+typedef void (*bt_shell_prompt_input_func) (const char *input, void *user_data);
+typedef bool (*bt_shell_menu_exists_t) (const struct bt_shell_menu *menu);
+
+struct bt_shell_menu_entry {
+ const char *cmd;
+ const char *arg;
+ bt_shell_menu_cb_t func;
+ const char *desc;
+ bt_shell_menu_gen_t gen;
+ bt_shell_menu_disp_t disp;
+ bt_shell_menu_exists_t exists;
+};
+
+struct bt_shell_menu {
+ const char *name;
+ const char *desc;
+ const struct bt_shell_menu_entry entries[];
+};
+
+struct bt_shell_opt {
+ const struct option *options;
+ size_t optno;
+ const char *optstr;
+ const char ***optarg;
+ const char **help;
+};
+
+void bt_shell_init(int argc, char **argv, const struct bt_shell_opt *opt);
+
+void bt_shell_run(void);
+
+bool bt_shell_set_menu(const struct bt_shell_menu *menu);
+
+bool bt_shell_add_submenu(const struct bt_shell_menu *menu);
+
+bool bt_shell_remove_submenu(const struct bt_shell_menu *menu);
+
+void bt_shell_set_prompt(const char *string);
+
+void bt_shell_printf(const char *fmt,
+ ...) __attribute__((format(printf, 1, 2)));
+void bt_shell_hexdump(const unsigned char *buf, size_t len);
+
+void bt_shell_prompt_input(const char *label, const char *msg,
+ bt_shell_prompt_input_func func, void *user_data);
+int bt_shell_release_prompt(const char *input);
+
+bool bt_shell_attach(int fd);
+bool bt_shell_detach(void);
+
+void bt_shell_cleanup(void);
#include <stdio.h>
#include <ctype.h>
#include <stdarg.h>
+#include <stdbool.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
char filename[PATH_MAX];
struct stat st;
- snprintf(filename, sizeof(filename), "%s/%s", parent, name);
+ snprintf(filename, PATH_MAX, "%s/%s", parent, name);
if (lstat(filename, &st) == 0 && S_ISDIR(st.st_mode))
return DT_DIR;
*bitmap &= ~(1 << (id - 1));
}
+
+static const struct {
+ uint16_t uuid;
+ const char *str;
+} uuid16_table[] = {
+ { 0x0001, "SDP" },
+ { 0x0003, "RFCOMM" },
+ { 0x0005, "TCS-BIN" },
+ { 0x0007, "ATT" },
+ { 0x0008, "OBEX" },
+ { 0x000f, "BNEP" },
+ { 0x0010, "UPNP" },
+ { 0x0011, "HIDP" },
+ { 0x0012, "Hardcopy Control Channel" },
+ { 0x0014, "Hardcopy Data Channel" },
+ { 0x0016, "Hardcopy Notification" },
+ { 0x0017, "AVCTP" },
+ { 0x0019, "AVDTP" },
+ { 0x001b, "CMTP" },
+ { 0x001e, "MCAP Control Channel" },
+ { 0x001f, "MCAP Data Channel" },
+ { 0x0100, "L2CAP" },
+ /* 0x0101 to 0x0fff undefined */
+ { 0x1000, "Service Discovery Server Service Class" },
+ { 0x1001, "Browse Group Descriptor Service Class" },
+ { 0x1002, "Public Browse Root" },
+ /* 0x1003 to 0x1100 undefined */
+ { 0x1101, "Serial Port" },
+ { 0x1102, "LAN Access Using PPP" },
+ { 0x1103, "Dialup Networking" },
+ { 0x1104, "IrMC Sync" },
+ { 0x1105, "OBEX Object Push" },
+ { 0x1106, "OBEX File Transfer" },
+ { 0x1107, "IrMC Sync Command" },
+ { 0x1108, "Headset" },
+ { 0x1109, "Cordless Telephony" },
+ { 0x110a, "Audio Source" },
+ { 0x110b, "Audio Sink" },
+ { 0x110c, "A/V Remote Control Target" },
+ { 0x110d, "Advanced Audio Distribution" },
+ { 0x110e, "A/V Remote Control" },
+ { 0x110f, "A/V Remote Control Controller" },
+ { 0x1110, "Intercom" },
+ { 0x1111, "Fax" },
+ { 0x1112, "Headset AG" },
+ { 0x1113, "WAP" },
+ { 0x1114, "WAP Client" },
+ { 0x1115, "PANU" },
+ { 0x1116, "NAP" },
+ { 0x1117, "GN" },
+ { 0x1118, "Direct Printing" },
+ { 0x1119, "Reference Printing" },
+ { 0x111a, "Basic Imaging Profile" },
+ { 0x111b, "Imaging Responder" },
+ { 0x111c, "Imaging Automatic Archive" },
+ { 0x111d, "Imaging Referenced Objects" },
+ { 0x111e, "Handsfree" },
+ { 0x111f, "Handsfree Audio Gateway" },
+ { 0x1120, "Direct Printing Refrence Objects Service" },
+ { 0x1121, "Reflected UI" },
+ { 0x1122, "Basic Printing" },
+ { 0x1123, "Printing Status" },
+ { 0x1124, "Human Interface Device Service" },
+ { 0x1125, "Hardcopy Cable Replacement" },
+ { 0x1126, "HCR Print" },
+ { 0x1127, "HCR Scan" },
+ { 0x1128, "Common ISDN Access" },
+ /* 0x1129 and 0x112a undefined */
+ { 0x112d, "SIM Access" },
+ { 0x112e, "Phonebook Access Client" },
+ { 0x112f, "Phonebook Access Server" },
+ { 0x1130, "Phonebook Access" },
+ { 0x1131, "Headset HS" },
+ { 0x1132, "Message Access Server" },
+ { 0x1133, "Message Notification Server" },
+ { 0x1134, "Message Access Profile" },
+ { 0x1135, "GNSS" },
+ { 0x1136, "GNSS Server" },
+ { 0x1137, "3D Display" },
+ { 0x1138, "3D Glasses" },
+ { 0x1139, "3D Synchronization" },
+ { 0x113a, "MPS Profile" },
+ { 0x113b, "MPS Service" },
+ /* 0x113c to 0x11ff undefined */
+ { 0x1200, "PnP Information" },
+ { 0x1201, "Generic Networking" },
+ { 0x1202, "Generic File Transfer" },
+ { 0x1203, "Generic Audio" },
+ { 0x1204, "Generic Telephony" },
+ { 0x1205, "UPNP Service" },
+ { 0x1206, "UPNP IP Service" },
+ { 0x1300, "UPNP IP PAN" },
+ { 0x1301, "UPNP IP LAP" },
+ { 0x1302, "UPNP IP L2CAP" },
+ { 0x1303, "Video Source" },
+ { 0x1304, "Video Sink" },
+ { 0x1305, "Video Distribution" },
+ /* 0x1306 to 0x13ff undefined */
+ { 0x1400, "HDP" },
+ { 0x1401, "HDP Source" },
+ { 0x1402, "HDP Sink" },
+ /* 0x1403 to 0x17ff undefined */
+ { 0x1800, "Generic Access Profile" },
+ { 0x1801, "Generic Attribute Profile" },
+ { 0x1802, "Immediate Alert" },
+ { 0x1803, "Link Loss" },
+ { 0x1804, "Tx Power" },
+ { 0x1805, "Current Time Service" },
+ { 0x1806, "Reference Time Update Service" },
+ { 0x1807, "Next DST Change Service" },
+ { 0x1808, "Glucose" },
+ { 0x1809, "Health Thermometer" },
+ { 0x180a, "Device Information" },
+ /* 0x180b and 0x180c undefined */
+ { 0x180d, "Heart Rate" },
+ { 0x180e, "Phone Alert Status Service" },
+ { 0x180f, "Battery Service" },
+ { 0x1810, "Blood Pressure" },
+ { 0x1811, "Alert Notification Service" },
+ { 0x1812, "Human Interface Device" },
+ { 0x1813, "Scan Parameters" },
+ { 0x1814, "Running Speed and Cadence" },
+ { 0x1815, "Automation IO" },
+ { 0x1816, "Cycling Speed and Cadence" },
+ /* 0x1817 undefined */
+ { 0x1818, "Cycling Power" },
+ { 0x1819, "Location and Navigation" },
+ { 0x181a, "Environmental Sensing" },
+ { 0x181b, "Body Composition" },
+ { 0x181c, "User Data" },
+ { 0x181d, "Weight Scale" },
+ { 0x181e, "Bond Management" },
+ { 0x181f, "Continuous Glucose Monitoring" },
+ { 0x1820, "Internet Protocol Support" },
+ { 0x1821, "Indoor Positioning" },
+ { 0x1822, "Pulse Oximeter" },
+ { 0x1823, "HTTP Proxy" },
+ { 0x1824, "Transport Discovery" },
+ { 0x1825, "Object Transfer" },
+ { 0x1826, "Fitness Machine" },
+ { 0x1827, "Mesh Provisioning" },
+ { 0x1828, "Mesh Proxy" },
+ /* 0x1829 to 0x27ff undefined */
+ { 0x2800, "Primary Service" },
+ { 0x2801, "Secondary Service" },
+ { 0x2802, "Include" },
+ { 0x2803, "Characteristic" },
+ /* 0x2804 to 0x28ff undefined */
+ { 0x2900, "Characteristic Extended Properties" },
+ { 0x2901, "Characteristic User Description" },
+ { 0x2902, "Client Characteristic Configuration" },
+ { 0x2903, "Server Characteristic Configuration" },
+ { 0x2904, "Characteristic Format" },
+ { 0x2905, "Characteristic Aggregate Formate" },
+ { 0x2906, "Valid Range" },
+ { 0x2907, "External Report Reference" },
+ { 0x2908, "Report Reference" },
+ { 0x2909, "Number of Digitals" },
+ { 0x290a, "Value Trigger Setting" },
+ { 0x290b, "Environmental Sensing Configuration" },
+ { 0x290c, "Environmental Sensing Measurement" },
+ { 0x290d, "Environmental Sensing Trigger Setting" },
+ { 0x290e, "Time Trigger Setting" },
+ /* 0x290f to 0x29ff undefined */
+ { 0x2a00, "Device Name" },
+ { 0x2a01, "Appearance" },
+ { 0x2a02, "Peripheral Privacy Flag" },
+ { 0x2a03, "Reconnection Address" },
+ { 0x2a04, "Peripheral Preferred Connection Parameters" },
+ { 0x2a05, "Service Changed" },
+ { 0x2a06, "Alert Level" },
+ { 0x2a07, "Tx Power Level" },
+ { 0x2a08, "Date Time" },
+ { 0x2a09, "Day of Week" },
+ { 0x2a0a, "Day Date Time" },
+ /* 0x2a0b undefined */
+ { 0x2a0c, "Exact Time 256" },
+ { 0x2a0d, "DST Offset" },
+ { 0x2a0e, "Time Zone" },
+ { 0x2a0f, "Local Time Information" },
+ /* 0x2a10 undefined */
+ { 0x2a11, "Time with DST" },
+ { 0x2a12, "Time Accuracy" },
+ { 0x2a13, "Time Source" },
+ { 0x2a14, "Reference Time Information" },
+ /* 0x2a15 undefined */
+ { 0x2a16, "Time Update Control Point" },
+ { 0x2a17, "Time Update State" },
+ { 0x2a18, "Glucose Measurement" },
+ { 0x2a19, "Battery Level" },
+ /* 0x2a1a and 0x2a1b undefined */
+ { 0x2a1c, "Temperature Measurement" },
+ { 0x2a1d, "Temperature Type" },
+ { 0x2a1e, "Intermediate Temperature" },
+ /* 0x2a1f and 0x2a20 undefined */
+ { 0x2a21, "Measurement Interval" },
+ { 0x2a22, "Boot Keyboard Input Report" },
+ { 0x2a23, "System ID" },
+ { 0x2a24, "Model Number String" },
+ { 0x2a25, "Serial Number String" },
+ { 0x2a26, "Firmware Revision String" },
+ { 0x2a27, "Hardware Revision String" },
+ { 0x2a28, "Software Revision String" },
+ { 0x2a29, "Manufacturer Name String" },
+ { 0x2a2a, "IEEE 11073-20601 Regulatory Cert. Data List" },
+ { 0x2a2b, "Current Time" },
+ { 0x2a2c, "Magnetic Declination" },
+ /* 0x2a2d to 0x2a30 undefined */
+ { 0x2a31, "Scan Refresh" },
+ { 0x2a32, "Boot Keyboard Output Report" },
+ { 0x2a33, "Boot Mouse Input Report" },
+ { 0x2a34, "Glucose Measurement Context" },
+ { 0x2a35, "Blood Pressure Measurement" },
+ { 0x2a36, "Intermediate Cuff Pressure" },
+ { 0x2a37, "Heart Rate Measurement" },
+ { 0x2a38, "Body Sensor Location" },
+ { 0x2a39, "Heart Rate Control Point" },
+ /* 0x2a3a to 0x2a3e undefined */
+ { 0x2a3f, "Alert Status" },
+ { 0x2a40, "Ringer Control Point" },
+ { 0x2a41, "Ringer Setting" },
+ { 0x2a42, "Alert Category ID Bit Mask" },
+ { 0x2a43, "Alert Category ID" },
+ { 0x2a44, "Alert Notification Control Point" },
+ { 0x2a45, "Unread Alert Status" },
+ { 0x2a46, "New Alert" },
+ { 0x2a47, "Supported New Alert Category" },
+ { 0x2a48, "Supported Unread Alert Category" },
+ { 0x2a49, "Blood Pressure Feature" },
+ { 0x2a4a, "HID Information" },
+ { 0x2a4b, "Report Map" },
+ { 0x2a4c, "HID Control Point" },
+ { 0x2a4d, "Report" },
+ { 0x2a4e, "Protocol Mode" },
+ { 0x2a4f, "Scan Interval Window" },
+ { 0x2a50, "PnP ID" },
+ { 0x2a51, "Glucose Feature" },
+ { 0x2a52, "Record Access Control Point" },
+ { 0x2a53, "RSC Measurement" },
+ { 0x2a54, "RSC Feature" },
+ { 0x2a55, "SC Control Point" },
+ { 0x2a56, "Digital" },
+ /* 0x2a57 undefined */
+ { 0x2a58, "Analog" },
+ /* 0x2a59 undefined */
+ { 0x2a5a, "Aggregate" },
+ { 0x2a5b, "CSC Measurement" },
+ { 0x2a5c, "CSC Feature" },
+ { 0x2a5d, "Sensor Location" },
+ /* 0x2a5e to 0x2a62 undefined */
+ { 0x2a63, "Cycling Power Measurement" },
+ { 0x2a64, "Cycling Power Vector" },
+ { 0x2a65, "Cycling Power Feature" },
+ { 0x2a66, "Cycling Power Control Point" },
+ { 0x2a67, "Location and Speed" },
+ { 0x2a68, "Navigation" },
+ { 0x2a69, "Position Quality" },
+ { 0x2a6a, "LN Feature" },
+ { 0x2a6b, "LN Control Point" },
+ { 0x2a6c, "Elevation" },
+ { 0x2a6d, "Pressure" },
+ { 0x2a6e, "Temperature" },
+ { 0x2a6f, "Humidity" },
+ { 0x2a70, "True Wind Speed" },
+ { 0x2a71, "True Wind Direction" },
+ { 0x2a72, "Apparent Wind Speed" },
+ { 0x2a73, "Apparent Wind Direction" },
+ { 0x2a74, "Gust Factor" },
+ { 0x2a75, "Pollen Concentration" },
+ { 0x2a76, "UV Index" },
+ { 0x2a77, "Irradiance" },
+ { 0x2a78, "Rainfall" },
+ { 0x2a79, "Wind Chill" },
+ { 0x2a7a, "Heat Index" },
+ { 0x2a7b, "Dew Point" },
+ { 0x2a7c, "Trend" },
+ { 0x2a7d, "Descriptor Value Changed" },
+ { 0x2a7e, "Aerobic Heart Rate Lower Limit" },
+ { 0x2a7f, "Aerobic Threshold" },
+ { 0x2a80, "Age" },
+ { 0x2a81, "Anaerobic Heart Rate Lower Limit" },
+ { 0x2a82, "Anaerobic Heart Rate Upper Limit" },
+ { 0x2a83, "Anaerobic Threshold" },
+ { 0x2a84, "Aerobic Heart Rate Upper Limit" },
+ { 0x2a85, "Date of Birth" },
+ { 0x2a86, "Date of Threshold Assessment" },
+ { 0x2a87, "Email Address" },
+ { 0x2a88, "Fat Burn Heart Rate Lower Limit" },
+ { 0x2a89, "Fat Burn Heart Rate Upper Limit" },
+ { 0x2a8a, "First Name" },
+ { 0x2a8b, "Five Zone Heart Rate Limits" },
+ { 0x2a8c, "Gender" },
+ { 0x2a8d, "Heart Rate Max" },
+ { 0x2a8e, "Height" },
+ { 0x2a8f, "Hip Circumference" },
+ { 0x2a90, "Last Name" },
+ { 0x2a91, "Maximum Recommended Heart Rate" },
+ { 0x2a92, "Resting Heart Rate" },
+ { 0x2a93, "Sport Type for Aerobic/Anaerobic Thresholds" },
+ { 0x2a94, "Three Zone Heart Rate Limits" },
+ { 0x2a95, "Two Zone Heart Rate Limit" },
+ { 0x2a96, "VO2 Max" },
+ { 0x2a97, "Waist Circumference" },
+ { 0x2a98, "Weight" },
+ { 0x2a99, "Database Change Increment" },
+ { 0x2a9a, "User Index" },
+ { 0x2a9b, "Body Composition Feature" },
+ { 0x2a9c, "Body Composition Measurement" },
+ { 0x2a9d, "Weight Measurement" },
+ { 0x2a9e, "Weight Scale Feature" },
+ { 0x2a9f, "User Control Point" },
+ { 0x2aa0, "Magnetic Flux Density - 2D" },
+ { 0x2aa1, "Magnetic Flux Density - 3D" },
+ { 0x2aa2, "Language" },
+ { 0x2aa3, "Barometric Pressure Trend" },
+ { 0x2aa4, "Bond Management Control Point" },
+ { 0x2aa5, "Bond Management Feature" },
+ { 0x2aa6, "Central Address Resolution" },
+ { 0x2aa7, "CGM Measurement" },
+ { 0x2aa8, "CGM Feature" },
+ { 0x2aa9, "CGM Status" },
+ { 0x2aaa, "CGM Session Start Time" },
+ { 0x2aab, "CGM Session Run Time" },
+ { 0x2aac, "CGM Specific Ops Control Point" },
+ { 0x2aad, "Indoor Positioning Configuration" },
+ { 0x2aae, "Latitude" },
+ { 0x2aaf, "Longitude" },
+ { 0x2ab0, "Local North Coordinate" },
+ { 0x2ab1, "Local East Coordinate" },
+ { 0x2ab2, "Floor Number" },
+ { 0x2ab3, "Altitude" },
+ { 0x2ab4, "Uncertainty" },
+ { 0x2ab5, "Location Name" },
+ { 0x2ab6, "URI" },
+ { 0x2ab7, "HTTP Headers" },
+ { 0x2ab8, "HTTP Status Code" },
+ { 0x2ab9, "HTTP Entity Body" },
+ { 0x2aba, "HTTP Control Point" },
+ { 0x2abb, "HTTPS Security" },
+ { 0x2abc, "TDS Control Point" },
+ { 0x2abd, "OTS Feature" },
+ { 0x2abe, "Object Name" },
+ { 0x2abf, "Object Type" },
+ { 0x2ac0, "Object Size" },
+ { 0x2ac1, "Object First-Created" },
+ { 0x2ac2, "Object Last-Modified" },
+ { 0x2ac3, "Object ID" },
+ { 0x2ac4, "Object Properties" },
+ { 0x2ac5, "Object Action Control Point" },
+ { 0x2ac6, "Object List Control Point" },
+ { 0x2ac7, "Object List Filter" },
+ { 0x2ac8, "Object Changed" },
+ { 0x2ac9, "Resolvable Private Address Only" },
+ /* 0x2aca and 0x2acb undefined */
+ { 0x2acc, "Fitness Machine Feature" },
+ { 0x2acd, "Treadmill Data" },
+ { 0x2ace, "Cross Trainer Data" },
+ { 0x2acf, "Step Climber Data" },
+ { 0x2ad0, "Stair Climber Data" },
+ { 0x2ad1, "Rower Data" },
+ { 0x2ad2, "Indoor Bike Data" },
+ { 0x2ad3, "Training Status" },
+ { 0x2ad4, "Supported Speed Range" },
+ { 0x2ad5, "Supported Inclination Range" },
+ { 0x2ad6, "Supported Resistance Level Range" },
+ { 0x2ad7, "Supported Heart Rate Range" },
+ { 0x2ad8, "Supported Power Range" },
+ { 0x2ad9, "Fitness Machine Control Point" },
+ { 0x2ada, "Fitness Machine Status" },
+ { 0x2adb, "Mesh Provisioning Data In" },
+ { 0x2adc, "Mesh Provisioning Data Out" },
+ { 0x2add, "Mesh Proxy Data In" },
+ { 0x2ade, "Mesh Proxy Data Out" },
+ /* vendor defined */
+ { 0xfeff, "GN Netcom" },
+ { 0xfefe, "GN ReSound A/S" },
+ { 0xfefd, "Gimbal, Inc." },
+ { 0xfefc, "Gimbal, Inc." },
+ { 0xfefb, "Stollmann E+V GmbH" },
+ { 0xfefa, "PayPal, Inc." },
+ { 0xfef9, "PayPal, Inc." },
+ { 0xfef8, "Aplix Corporation" },
+ { 0xfef7, "Aplix Corporation" },
+ { 0xfef6, "Wicentric, Inc." },
+ { 0xfef5, "Dialog Semiconductor GmbH" },
+ { 0xfef4, "Google" },
+ { 0xfef3, "Google" },
+ { 0xfef2, "CSR" },
+ { 0xfef1, "CSR" },
+ { 0xfef0, "Intel" },
+ { 0xfeef, "Polar Electro Oy" },
+ { 0xfeee, "Polar Electro Oy" },
+ { 0xfeed, "Tile, Inc." },
+ { 0xfeec, "Tile, Inc." },
+ { 0xfeeb, "Swirl Networks, Inc." },
+ { 0xfeea, "Swirl Networks, Inc." },
+ { 0xfee9, "Quintic Corp." },
+ { 0xfee8, "Quintic Corp." },
+ { 0xfee7, "Tencent Holdings Limited" },
+ { 0xfee6, "Seed Labs, Inc." },
+ { 0xfee5, "Nordic Semiconductor ASA" },
+ { 0xfee4, "Nordic Semiconductor ASA" },
+ { 0xfee3, "Anki, Inc." },
+ { 0xfee2, "Anki, Inc." },
+ { 0xfee1, "Anhui Huami Information Technology Co." },
+ { 0xfee0, "Anhui Huami Information Technology Co." },
+ { 0xfedf, "Design SHIFT" },
+ { 0xfede, "Coin, Inc." },
+ { 0xfedd, "Jawbone" },
+ { 0xfedc, "Jawbone" },
+ { 0xfedb, "Perka, Inc." },
+ { 0xfeda, "ISSC Technologies Corporation" },
+ { 0xfed9, "Pebble Technology Corporation" },
+ { 0xfed8, "Google" },
+ { 0xfed7, "Broadcom Corporation" },
+ { 0xfed6, "Broadcom Corporation" },
+ { 0xfed5, "Plantronics Inc." },
+ { 0xfed4, "Apple, Inc." },
+ { 0xfed3, "Apple, Inc." },
+ { 0xfed2, "Apple, Inc." },
+ { 0xfed1, "Apple, Inc." },
+ { 0xfed0, "Apple, Inc." },
+ { 0xfecf, "Apple, Inc." },
+ { 0xfece, "Apple, Inc." },
+ { 0xfecd, "Apple, Inc." },
+ { 0xfecc, "Apple, Inc." },
+ { 0xfecb, "Apple, Inc." },
+ { 0xfeca, "Apple, Inc." },
+ { 0xfec9, "Apple, Inc." },
+ { 0xfec8, "Apple, Inc." },
+ { 0xfec7, "Apple, Inc." },
+ { 0xfec6, "Kocomojo, LLC" },
+ { 0xfec5, "Realtek Semiconductor Corp." },
+ { 0xfec4, "PLUS Location Systems" },
+ { 0xfec3, "360fly, Inc." },
+ { 0xfec2, "Blue Spark Technologies, Inc." },
+ { 0xfec1, "KDDI Corporation" },
+ { 0xfec0, "KDDI Corporation" },
+ { 0xfebf, "Nod, Inc." },
+ { 0xfebe, "Bose Corporation" },
+ { 0xfebd, "Clover Network, Inc." },
+ { 0xfebc, "Dexcom, Inc." },
+ { 0xfebb, "adafruit industries" },
+ { 0xfeba, "Tencent Holdings Limited" },
+ { 0xfeb9, "LG Electronics" },
+ { 0xfeb8, "Facebook, Inc." },
+ { 0xfeb7, "Facebook, Inc." },
+ { 0xfeb6, "Vencer Co, Ltd" },
+ { 0xfeb5, "WiSilica Inc." },
+ { 0xfeb4, "WiSilica Inc." },
+ { 0xfeb3, "Taobao" },
+ { 0xfeb2, "Microsoft Corporation" },
+ { 0xfeb1, "Electronics Tomorrow Limited" },
+ { 0xfeb0, "Nest Labs Inc." },
+ { 0xfeaf, "Nest Labs Inc." },
+ { 0xfeae, "Nokia Corporation" },
+ { 0xfead, "Nokia Corporation" },
+ { 0xfeac, "Nokia Corporation" },
+ { 0xfeab, "Nokia Corporation" },
+ { 0xfeaa, "Google" },
+ { 0xfea9, "Savant Systems LLC" },
+ { 0xfea8, "Savant Systems LLC" },
+ { 0xfea7, "UTC Fire and Security" },
+ { 0xfea6, "GoPro, Inc." },
+ { 0xfea5, "GoPro, Inc." },
+ { 0xfea4, "Paxton Access Ltd" },
+ { 0xfea3, "ITT Industries" },
+ { 0xfea2, "Intrepid Control Systems, Inc." },
+ { 0xfea1, "Intrepid Control Systems, Inc." },
+ { 0xfea0, "Google" },
+ { 0xfe9f, "Google" },
+ { 0xfe9e, "Dialog Semiconductor B.V." },
+ { 0xfe9d, "Mobiquity Networks Inc" },
+ { 0xfe9c, "GSI Laboratories, Inc." },
+ { 0xfe9b, "Samsara Networks, Inc" },
+ { 0xfe9a, "Estimote" },
+ { 0xfe99, "Currant, Inc." },
+ { 0xfe98, "Currant, Inc." },
+ { 0xfe97, "Tesla Motor Inc." },
+ { 0xfe96, "Tesla Motor Inc." },
+ { 0xfe95, "Xiaomi Inc." },
+ { 0xfe94, "OttoQ Inc." },
+ { 0xfe93, "OttoQ Inc." },
+ { 0xfe92, "Jarden Safety & Security" },
+ { 0xfe91, "Shanghai Imilab Technology Co.,Ltd" },
+ { 0xfe90, "JUMA" },
+ { 0xfe8f, "CSR" },
+ { 0xfe8e, "ARM Ltd" },
+ { 0xfe8d, "Interaxon Inc." },
+ { 0xfe8c, "TRON Forum" },
+ { 0xfe8b, "Apple, Inc." },
+ { 0xfe8a, "Apple, Inc." },
+ { 0xfe89, "B&O Play A/S" },
+ { 0xfe88, "SALTO SYSTEMS S.L." },
+ { 0xfe87, "Qingdao Yeelink Information Technology Co., Ltd. ( 青岛亿联客信息技术有限公司 )" },
+ { 0xfe86, "HUAWEI Technologies Co., Ltd. ( 华为技术有限公司 )" },
+ { 0xfe85, "RF Digital Corp" },
+ { 0xfe84, "RF Digital Corp" },
+ { 0xfe83, "Blue Bite" },
+ { 0xfe82, "Medtronic Inc." },
+ { 0xfe81, "Medtronic Inc." },
+ { 0xfe80, "Doppler Lab" },
+ { 0xfe7f, "Doppler Lab" },
+ { 0xfe7e, "Awear Solutions Ltd" },
+ { 0xfe7d, "Aterica Health Inc." },
+ { 0xfe7c, "Stollmann E+V GmbH" },
+ { 0xfe7b, "Orion Labs, Inc." },
+ { 0xfe7a, "Bragi GmbH" },
+ { 0xfe79, "Zebra Technologies" },
+ { 0xfe78, "Hewlett-Packard Company" },
+ { 0xfe77, "Hewlett-Packard Company" },
+ { 0xfe76, "TangoMe" },
+ { 0xfe75, "TangoMe" },
+ { 0xfe74, "unwire" },
+ { 0xfe73, "St. Jude Medical, Inc." },
+ { 0xfe72, "St. Jude Medical, Inc." },
+ { 0xfe71, "Plume Design Inc" },
+ { 0xfe70, "Beijing Jingdong Century Trading Co., Ltd." },
+ { 0xfe6f, "LINE Corporation" },
+ { 0xfe6e, "The University of Tokyo" },
+ { 0xfe6d, "The University of Tokyo" },
+ { 0xfe6c, "TASER International, Inc." },
+ { 0xfe6b, "TASER International, Inc." },
+ { 0xfe6a, "Kontakt Micro-Location Sp. z o.o." },
+ { 0xfe69, "Qualcomm Life Inc" },
+ { 0xfe68, "Qualcomm Life Inc" },
+ { 0xfe67, "Lab Sensor Solutions" },
+ { 0xfe66, "Intel Corporation" },
+ { 0xfe65, "CHIPOLO d.o.o." },
+ { 0xfe64, "Siemens AG" },
+ { 0xfe63, "Connected Yard, Inc." },
+ { 0xfe62, "Indagem Tech LLC" },
+ { 0xfe61, "Logitech International SA" },
+ { 0xfe60, "Lierda Science & Technology Group Co., Ltd." },
+ { 0xfe5F, "Eyefi, Inc." },
+ { 0xfe5E, "Plastc Corporation" },
+ { 0xfe5D, "Grundfos A/S" },
+ { 0xfe5C, "million hunters GmbH" },
+ { 0xfe5B, "GT-tronics HK Ltd" },
+ { 0xfe5A, "Chronologics Corporation" },
+ { 0xfe59, "Nordic Semiconductor ASA" },
+ { 0xfe58, "Nordic Semiconductor ASA" },
+ { 0xfe57, "Dotted Labs" },
+ { 0xfe56, "Google Inc." },
+ { 0xfe55, "Google Inc." },
+ { 0xfe54, "Motiv, Inc." },
+ { 0xfe53, "3M" },
+ { 0xfe52, "SetPoint Medical" },
+ { 0xfe51, "SRAM" },
+ { 0xfe50, "Google Inc." },
+ { 0xfe4F, "Molekule, Inc." },
+ { 0xfe4E, "NTT docomo" },
+ { 0xfe4D, "Casambi Technologies Oy" },
+ { 0xfe4C, "Volkswagen AG" },
+ { 0xfe4B, "Koninklijke Philips N.V." },
+ { 0xfe4A, "OMRON HEALTHCARE Co., Ltd." },
+ { 0xfe49, "SenionLab AB" },
+ { 0xfe48, "General Motors" },
+ { 0xfe47, "General Motors" },
+ { 0xfe46, "B&O Play A/S" },
+ { 0xfe45, "Snapchat Inc" },
+ { 0xfe44, "SK Telecom" },
+ { 0xfe43, "Andreas Stihl AG & Co. KG" },
+ { 0xfe42, "Nets A/S" },
+ { 0xfe41, "Inugo Systems Limited" },
+ { 0xfe40, "Inugo Systems Limited" },
+ { 0xfe3F, "Friday Labs Limited" },
+ { 0xfe3E, "BD Medical" },
+ { 0xfe3D, "BD Medical" },
+ { 0xfe3C, "Alibaba" },
+ { 0xfe3B, "Dolby Laboratories" },
+ { 0xfe3A, "TTS Tooltechnic Systems AG & Co. KG" },
+ { 0xfe39, "TTS Tooltechnic Systems AG & Co. KG" },
+ { 0xfe38, "Spaceek LTD" },
+ { 0xfe37, "Spaceek LTD" },
+ { 0xfe36, "HUAWEI Technologies Co., Ltd" },
+ { 0xfe35, "HUAWEI Technologies Co., Ltd" },
+ { 0xfe34, "SmallLoop LLC" },
+ { 0xfe33, "CHIPOLO d.o.o." },
+ { 0xfe32, "Pro-Mark, Inc." },
+ { 0xfe31, "Volkswagen AG" },
+ { 0xfe30, "Volkswagen AG" },
+ { 0xfe2F, "CRESCO Wireless, Inc" },
+ { 0xfe2E, "ERi,Inc." },
+ { 0xfe2D, "SMART INNOVATION Co.,Ltd" },
+ { 0xfe2C, "Google Inc." },
+ { 0xfe2B, "ITT Industries" },
+ { 0xfe2A, "DaisyWorks, Inc." },
+ { 0xfe29, "Gibson Innovations" },
+ { 0xfe28, "Ayla Network" },
+ { 0xfe27, "Google Inc." },
+ { 0xfe26, "Google Inc." },
+ { 0xfe25, "Apple, Inc." },
+ { 0xfe24, "August Home Inc" },
+ { 0xfe23, "Zoll Medical Corporation" },
+ { 0xfe22, "Zoll Medical Corporation" },
+ { 0xfe21, "Bose Corporation" },
+ { 0xfe20, "Emerson" },
+ { 0xfe1F, "Garmin International, Inc." },
+ { 0xfe1E, "Smart Innovations Co., Ltd" },
+ { 0xfe1D, "Illuminati Instrument Corporation" },
+ { 0xfe1C, "NetMedia, Inc." },
+ /* SDO defined */
+ { 0xfffc, "AirFuel Alliance" },
+ { 0xfffe, "Alliance for Wireless Power (A4WP)" },
+ { 0xfffd, "Fast IDentity Online Alliance (FIDO)" },
+ { }
+};
+
+static const struct {
+ const char *uuid;
+ const char *str;
+} uuid128_table[] = {
+ { "a3c87500-8ed3-4bdf-8a39-a01bebede295",
+ "Eddystone Configuration Service" },
+ { "a3c87501-8ed3-4bdf-8a39-a01bebede295", "Capabilities" },
+ { "a3c87502-8ed3-4bdf-8a39-a01bebede295", "Active Slot" },
+ { "a3c87503-8ed3-4bdf-8a39-a01bebede295",
+ "Advertising Interval" },
+ { "a3c87504-8ed3-4bdf-8a39-a01bebede295", "Radio Tx Power" },
+ { "a3c87505-8ed3-4bdf-8a39-a01bebede295",
+ "(Advanced) Advertised Tx Power" },
+ { "a3c87506-8ed3-4bdf-8a39-a01bebede295", "Lock State" },
+ { "a3c87507-8ed3-4bdf-8a39-a01bebede295", "Unlock" },
+ { "a3c87508-8ed3-4bdf-8a39-a01bebede295", "Public ECDH Key" },
+ { "a3c87509-8ed3-4bdf-8a39-a01bebede295", "EID Identity Key" },
+ { "a3c8750a-8ed3-4bdf-8a39-a01bebede295", "ADV Slot Data" },
+ { "a3c8750b-8ed3-4bdf-8a39-a01bebede295",
+ "(Advanced) Factory reset" },
+ { "a3c8750c-8ed3-4bdf-8a39-a01bebede295",
+ "(Advanced) Remain Connectable" },
+ /* BBC micro:bit Bluetooth Profiles */
+ { "e95d0753-251d-470a-a062-fa1922dfa9a8",
+ "MicroBit Accelerometer Service" },
+ { "e95dca4b-251d-470a-a062-fa1922dfa9a8",
+ "MicroBit Accelerometer Data" },
+ { "e95dfb24-251d-470a-a062-fa1922dfa9a8",
+ "MicroBit Accelerometer Period" },
+ { "e95df2d8-251d-470a-a062-fa1922dfa9a8",
+ "MicroBit Magnetometer Service" },
+ { "e95dfb11-251d-470a-a062-fa1922dfa9a8",
+ "MicroBit Magnetometer Data" },
+ { "e95d386c-251d-470a-a062-fa1922dfa9a8",
+ "MicroBit Magnetometer Period" },
+ { "e95d9715-251d-470a-a062-fa1922dfa9a8",
+ "MicroBit Magnetometer Bearing" },
+ { "e95d9882-251d-470a-a062-fa1922dfa9a8",
+ "MicroBit Button Service" },
+ { "e95dda90-251d-470a-a062-fa1922dfa9a8",
+ "MicroBit Button A State" },
+ { "e95dda91-251d-470a-a062-fa1922dfa9a8",
+ "MicroBit Button B State" },
+ { "e95d127b-251d-470a-a062-fa1922dfa9a8",
+ "MicroBit IO PIN Service" },
+ { "e95d8d00-251d-470a-a062-fa1922dfa9a8", "MicroBit PIN Data" },
+ { "e95d5899-251d-470a-a062-fa1922dfa9a8",
+ "MicroBit PIN AD Configuration" },
+ { "e95dd822-251d-470a-a062-fa1922dfa9a8", "MicroBit PWM Control" },
+ { "e95dd91d-251d-470a-a062-fa1922dfa9a8", "MicroBit LED Service" },
+ { "e95d7b77-251d-470a-a062-fa1922dfa9a8", "MicroBit LED Matrix state" },
+ { "e95d93ee-251d-470a-a062-fa1922dfa9a8", "MicroBit LED Text" },
+ { "e95d0d2d-251d-470a-a062-fa1922dfa9a8", "MicroBit Scrolling Delay" },
+ { "e95d93af-251d-470a-a062-fa1922dfa9a8", "MicroBit Event Service" },
+ { "e95db84c-251d-470a-a062-fa1922dfa9a8", "MicroBit Requirements" },
+ { "e95d9775-251d-470a-a062-fa1922dfa9a8", "MicroBit Event Data" },
+ { "e95d23c4-251d-470a-a062-fa1922dfa9a8",
+ "MicroBit Client Requirements" },
+ { "e95d5404-251d-470a-a062-fa1922dfa9a8", "MicroBit Client Events" },
+ { "e95d93b0-251d-470a-a062-fa1922dfa9a8",
+ "MicroBit DFU Control Service" },
+ { "e95d93b1-251d-470a-a062-fa1922dfa9a8", "MicroBit DFU Control" },
+ { "e95d6100-251d-470a-a062-fa1922dfa9a8",
+ "MicroBit Temperature Service" },
+ { "e95d1b25-251d-470a-a062-fa1922dfa9a8",
+ "MicroBit Temperature Period" },
+ /* Nordic UART Port Emulation */
+ { "6e400001-b5a3-f393-e0a9-e50e24dcca9e", "Nordic UART Service" },
+ { "6e400002-b5a3-f393-e0a9-e50e24dcca9e", "Nordic UART TX" },
+ { "6e400003-b5a3-f393-e0a9-e50e24dcca9e", "Nordic UART RX" },
+ { }
+};
+
+const char *bt_uuid16_to_str(uint16_t uuid)
+{
+ int i;
+
+ for (i = 0; uuid16_table[i].str; i++) {
+ if (uuid16_table[i].uuid == uuid)
+ return uuid16_table[i].str;
+ }
+
+ return "Unknown";
+}
+
+const char *bt_uuid32_to_str(uint32_t uuid)
+{
+ if ((uuid & 0xffff0000) == 0x0000)
+ return bt_uuid16_to_str(uuid & 0x0000ffff);
+
+ return "Unknown";
+}
+
+const char *bt_uuidstr_to_str(const char *uuid)
+{
+ uint32_t val;
+ size_t len;
+ int i;
+
+ if (!uuid)
+ return NULL;
+
+ len = strlen(uuid);
+
+ if (len < 36) {
+ char *endptr = NULL;
+
+ val = strtol(uuid, &endptr, 0);
+ if (!endptr || *endptr != '\0')
+ return NULL;
+
+ if (val > UINT16_MAX)
+ return bt_uuid32_to_str(val);
+
+ return bt_uuid16_to_str(val);
+ }
+
+ if (len != 36)
+ return NULL;
+
+ for (i = 0; uuid128_table[i].str; i++) {
+ if (strcasecmp(uuid128_table[i].uuid, uuid) == 0)
+ return uuid128_table[i].str;
+ }
+
+ if (strncasecmp(uuid + 8, "-0000-1000-8000-00805f9b34fb", 28))
+ return "Vendor specific";
+
+ if (sscanf(uuid, "%08x-0000-1000-8000-00805f9b34fb", &val) != 1)
+ return NULL;
+
+ return bt_uuid32_to_str(val);
+}
+
+static const struct {
+ uint16_t val;
+ bool generic;
+ const char *str;
+} appearance_table[] = {
+ { 0, true, "Unknown" },
+ { 64, true, "Phone" },
+ { 128, true, "Computer" },
+ { 192, true, "Watch" },
+ { 193, false, "Sports Watch" },
+ { 256, true, "Clock" },
+ { 320, true, "Display" },
+ { 384, true, "Remote Control" },
+ { 448, true, "Eye-glasses" },
+ { 512, true, "Tag" },
+ { 576, true, "Keyring" },
+ { 640, true, "Media Player" },
+ { 704, true, "Barcode Scanner" },
+ { 768, true, "Thermometer" },
+ { 769, false, "Thermometer: Ear" },
+ { 832, true, "Heart Rate Sensor" },
+ { 833, false, "Heart Rate Belt" },
+ { 896, true, "Blood Pressure" },
+ { 897, false, "Blood Pressure: Arm" },
+ { 898, false, "Blood Pressure: Wrist" },
+ { 960, true, "Human Interface Device" },
+ { 961, false, "Keyboard" },
+ { 962, false, "Mouse" },
+ { 963, false, "Joystick" },
+ { 964, false, "Gamepad" },
+ { 965, false, "Digitizer Tablet" },
+ { 966, false, "Card Reader" },
+ { 967, false, "Digital Pen" },
+ { 968, false, "Barcode Scanner" },
+ { 1024, true, "Glucose Meter" },
+ { 1088, true, "Running Walking Sensor" },
+ { 1089, false, "Running Walking Sensor: In-Shoe" },
+ { 1090, false, "Running Walking Sensor: On-Shoe" },
+ { 1091, false, "Running Walking Sensor: On-Hip" },
+ { 1152, true, "Cycling" },
+ { 1153, false, "Cycling: Cycling Computer" },
+ { 1154, false, "Cycling: Speed Sensor" },
+ { 1155, false, "Cycling: Cadence Sensor" },
+ { 1156, false, "Cycling: Power Sensor" },
+ { 1157, false, "Cycling: Speed and Cadence Sensor" },
+ { 1216, true, "Undefined" },
+
+ { 3136, true, "Pulse Oximeter" },
+ { 3137, false, "Pulse Oximeter: Fingertip" },
+ { 3138, false, "Pulse Oximeter: Wrist Worn" },
+ { 3200, true, "Weight Scale" },
+ { 3264, true, "Undefined" },
+
+ { 5184, true, "Outdoor Sports Activity" },
+ { 5185, false, "Location Display Device" },
+ { 5186, false, "Location and Navigation Display Device" },
+ { 5187, false, "Location Pod" },
+ { 5188, false, "Location and Navigation Pod" },
+ { 5248, true, "Undefined" },
+ { }
+};
+
+const char *bt_appear_to_str(uint16_t appearance)
+{
+ const char *str = NULL;
+ int i, type = 0;
+
+ for (i = 0; appearance_table[i].str; i++) {
+ if (appearance_table[i].generic) {
+ if (appearance < appearance_table[i].val)
+ break;
+ type = i;
+ }
+
+ if (appearance_table[i].val == appearance) {
+ str = appearance_table[i].str;
+ break;
+ }
+ }
+
+ if (!str)
+ str = appearance_table[type].str;
+
+ return str;
+}
uint8_t util_get_uid(unsigned int *bitmap, uint8_t max);
void util_clear_uid(unsigned int *bitmap, uint8_t id);
+const char *bt_uuid16_to_str(uint16_t uuid);
+const char *bt_uuid32_to_str(uint32_t uuid);
+const char *bt_uuidstr_to_str(const char *uuid);
+const char *bt_appear_to_str(uint16_t appearance);
+
static inline int8_t get_s8(const void *ptr)
{
return *((int8_t *) ptr);
+++ /dev/null
-#!/usr/bin/python
-
-import dbus
-import dbus.exceptions
-import dbus.mainloop.glib
-import dbus.service
-
-import array
-import gobject
-
-from random import randint
-
-mainloop = None
-
-BLUEZ_SERVICE_NAME = 'org.bluez'
-LE_ADVERTISING_MANAGER_IFACE = 'org.bluez.LEAdvertisingManager1'
-DBUS_OM_IFACE = 'org.freedesktop.DBus.ObjectManager'
-DBUS_PROP_IFACE = 'org.freedesktop.DBus.Properties'
-
-LE_ADVERTISEMENT_IFACE = 'org.bluez.LEAdvertisement1'
-
-
-class InvalidArgsException(dbus.exceptions.DBusException):
- _dbus_error_name = 'org.freedesktop.DBus.Error.InvalidArgs'
-
-
-class NotSupportedException(dbus.exceptions.DBusException):
- _dbus_error_name = 'org.bluez.Error.NotSupported'
-
-
-class NotPermittedException(dbus.exceptions.DBusException):
- _dbus_error_name = 'org.bluez.Error.NotPermitted'
-
-
-class InvalidValueLengthException(dbus.exceptions.DBusException):
- _dbus_error_name = 'org.bluez.Error.InvalidValueLength'
-
-
-class FailedException(dbus.exceptions.DBusException):
- _dbus_error_name = 'org.bluez.Error.Failed'
-
-
-class Advertisement(dbus.service.Object):
- PATH_BASE = '/org/bluez/example/advertisement'
-
- def __init__(self, bus, index, advertising_type):
- self.path = self.PATH_BASE + str(index)
- self.bus = bus
- self.ad_type = advertising_type
- self.service_uuids = None
- self.manufacturer_data = None
- self.solicit_uuids = None
- self.service_data = None
- dbus.service.Object.__init__(self, bus, self.path)
-
- def get_properties(self):
- properties = dict()
- properties['Type'] = self.ad_type
- if self.service_uuids is not None:
- properties['ServiceUUIDs'] = dbus.Array(self.service_uuids,
- signature='s')
- if self.solicit_uuids is not None:
- properties['SolicitUUIDs'] = dbus.Array(self.solicit_uuids,
- signature='s')
- if self.manufacturer_data is not None:
- properties['ManufacturerData'] = dbus.Dictionary(
- self.manufacturer_data, signature='qay')
- if self.service_data is not None:
- properties['ServiceData'] = dbus.Dictionary(self.service_data,
- signature='say')
- return {LE_ADVERTISEMENT_IFACE: properties}
-
- def get_path(self):
- return dbus.ObjectPath(self.path)
-
- def add_service_uuid(self, uuid):
- if not self.service_uuids:
- self.service_uuids = []
- self.service_uuids.append(uuid)
-
- def add_solicit_uuid(self, uuid):
- if not self.solicit_uuids:
- self.solicit_uuids = []
- self.solicit_uuids.append(uuid)
-
- def add_manufacturer_data(self, manuf_code, data):
- if not self.manufacturer_data:
- self.manufacturer_data = dict()
- self.manufacturer_data[manuf_code] = data
-
- def add_service_data(self, uuid, data):
- if not self.service_data:
- self.service_data = dict()
- self.service_data[uuid] = data
-
- @dbus.service.method(DBUS_PROP_IFACE,
- in_signature='s',
- out_signature='a{sv}')
- def GetAll(self, interface):
- print 'GetAll'
- if interface != LE_ADVERTISEMENT_IFACE:
- raise InvalidArgsException()
- print 'returning props'
- return self.get_properties()[LE_ADVERTISEMENT_IFACE]
-
- @dbus.service.method(LE_ADVERTISEMENT_IFACE,
- in_signature='',
- out_signature='')
- def Release(self):
- print '%s: Released!' % self.path
-
-class TestAdvertisement(Advertisement):
-
- def __init__(self, bus, index):
- Advertisement.__init__(self, bus, index, 'broadcast')
- self.add_service_uuid('0000180D-0000-1000-8000-00805F9B34FB')
- self.add_service_uuid('0000180F-0000-1000-8000-00805F9B34FB')
- self.add_manufacturer_data(0xffff, [0x00, 0x01, 0x02, 0x03, 0x04])
- self.add_service_data('00009999-0000-1000-8000-00805F9B34FB',
- [0x00, 0x01, 0x02, 0x03, 0x04])
-
-
-def register_ad_cb():
- print 'Advertisement registered'
-
-
-def register_ad_error_cb(error):
- print 'Failed to register advertisement: ' + str(error)
- mainloop.quit()
-
-
-def find_adapter(bus):
- remote_om = dbus.Interface(bus.get_object(BLUEZ_SERVICE_NAME, '/'),
- DBUS_OM_IFACE)
- objects = remote_om.GetManagedObjects()
-
- for o, props in objects.iteritems():
- if LE_ADVERTISING_MANAGER_IFACE in props:
- return o
-
- return None
-
-
-def main():
- global mainloop
-
- dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
-
- bus = dbus.SystemBus()
-
- adapter = find_adapter(bus)
- if not adapter:
- print 'LEAdvertisingManager1 interface not found'
- return
-
- ad_manager = dbus.Interface(bus.get_object(BLUEZ_SERVICE_NAME, adapter),
- LE_ADVERTISING_MANAGER_IFACE)
-
- test_advertisement = TestAdvertisement(bus, 0)
-
- mainloop = gobject.MainLoop()
-
- ad_manager.RegisterAdvertisement(test_advertisement.get_path(), {},
- reply_handler=register_ad_cb,
- error_handler=register_ad_error_cb)
-
- mainloop.run()
-
-if __name__ == '__main__':
- main()
self.manufacturer_data = None
self.solicit_uuids = None
self.service_data = None
+ self.local_name = None
self.include_tx_power = None
dbus.service.Object.__init__(self, bus, self.path)
if self.service_data is not None:
properties['ServiceData'] = dbus.Dictionary(self.service_data,
signature='sv')
+ if self.local_name is not None:
+ properties['LocalName'] = dbus.String(self.local_name)
if self.include_tx_power is not None:
properties['IncludeTxPower'] = dbus.Boolean(self.include_tx_power)
return {LE_ADVERTISEMENT_IFACE: properties}
self.service_data = dbus.Dictionary({}, signature='sv')
self.service_data[uuid] = dbus.Array(data, signature='y')
+ def add_local_name(self, name):
+ if not self.local_name:
+ self.local_name = ""
+ self.local_name = dbus.String(name)
+
@dbus.service.method(DBUS_PROP_IFACE,
in_signature='s',
out_signature='a{sv}')
self.add_service_uuid('180F')
self.add_manufacturer_data(0xffff, [0x00, 0x01, 0x02, 0x03, 0x04])
self.add_service_data('9999', [0x00, 0x01, 0x02, 0x03, 0x04])
+ self.add_local_name('TestAdvertisement')
self.include_tx_power = True
{ 'Value': [dbus.Byte(self.battery_lvl)] }, [])
def drain_battery(self):
+ if not self.notifying:
+ return True
if self.battery_lvl > 0:
self.battery_lvl -= 2
if self.battery_lvl < 0:
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011-2012 Intel Corporation
+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <getopt.h>
+
+#include "lib/bluetooth.h"
+#include "lib/mgmt.h"
+
+#include "monitor/bt.h"
+#include "src/shared/mainloop.h"
+#include "src/shared/util.h"
+#include "src/shared/mgmt.h"
+#include "src/shared/hci.h"
+#include "src/shared/crypto.h"
+
+#define PEER_ADDR_TYPE 0x00
+#define PEER_ADDR "\x00\x00\x00\x00\x00\x00"
+
+#define ADV_IRK "\x69\x30\xde\xc3\x8f\x84\x74\x14" \
+ "\xe1\x23\x99\xc1\xca\x9a\xc3\x31"
+#define SCAN_IRK "\xfa\x73\x09\x11\x3f\x03\x37\x0f" \
+ "\xf4\xf9\x93\x1e\xf9\xa3\x63\xa6"
+
+static struct mgmt *mgmt;
+static uint16_t index1 = MGMT_INDEX_NONE;
+static uint16_t index2 = MGMT_INDEX_NONE;
+
+static struct bt_crypto *crypto;
+static struct bt_hci *adv_dev;
+static struct bt_hci *scan_dev;
+
+static void print_rpa(const uint8_t addr[6])
+{
+ printf(" Address: %02x:%02x:%02x:%02x:%02x:%02x\n",
+ addr[5], addr[4], addr[3],
+ addr[2], addr[1], addr[0]);
+ printf(" Random: %02x%02x%02x\n", addr[3], addr[4], addr[5]);
+ printf(" Hash: %02x%02x%02x\n", addr[0], addr[1], addr[2]);
+}
+
+static void scan_le_adv_report(const void *data, uint8_t size,
+ void *user_data)
+{
+ const struct bt_hci_evt_le_adv_report *evt = data;
+
+ if (evt->addr_type == 0x01 && (evt->addr[5] & 0xc0) == 0x40) {
+ uint8_t hash[3], irk[16];
+
+ memcpy(irk, ADV_IRK, 16);
+ bt_crypto_ah(crypto, irk, evt->addr + 3, hash);
+
+ if (!memcmp(evt->addr, hash, 3)) {
+ printf("Received advertising report\n");
+ print_rpa(evt->addr);
+
+ memcpy(irk, ADV_IRK, 16);
+ bt_crypto_ah(crypto, irk, evt->addr + 3, hash);
+
+ printf(" -> Computed hash: %02x%02x%02x\n",
+ hash[0], hash[1], hash[2]);
+
+ mainloop_quit();
+ }
+ }
+}
+
+static void scan_le_meta_event(const void *data, uint8_t size,
+ void *user_data)
+{
+ uint8_t evt_code = ((const uint8_t *) data)[0];
+
+ switch (evt_code) {
+ case BT_HCI_EVT_LE_ADV_REPORT:
+ scan_le_adv_report(data + 1, size - 1, user_data);
+ break;
+ }
+}
+
+static void scan_enable_callback(const void *data, uint8_t size,
+ void *user_data)
+{
+}
+
+static void adv_enable_callback(const void *data, uint8_t size,
+ void *user_data)
+{
+ struct bt_hci_cmd_le_set_scan_parameters cmd4;
+ struct bt_hci_cmd_le_set_scan_enable cmd5;
+
+ cmd4.type = 0x00; /* Passive scanning */
+ cmd4.interval = cpu_to_le16(0x0010);
+ cmd4.window = cpu_to_le16(0x0010);
+ cmd4.own_addr_type = 0x00; /* Use public address */
+ cmd4.filter_policy = 0x00;
+
+ bt_hci_send(scan_dev, BT_HCI_CMD_LE_SET_SCAN_PARAMETERS,
+ &cmd4, sizeof(cmd4), NULL, NULL, NULL);
+
+ cmd5.enable = 0x01;
+ cmd5.filter_dup = 0x01;
+
+ bt_hci_send(scan_dev, BT_HCI_CMD_LE_SET_SCAN_ENABLE,
+ &cmd5, sizeof(cmd5),
+ scan_enable_callback, NULL, NULL);
+}
+
+static void adv_le_evtmask_callback(const void *data, uint8_t size,
+ void *user_data)
+{
+ struct bt_hci_cmd_le_set_resolv_timeout cmd0;
+ struct bt_hci_cmd_le_add_to_resolv_list cmd1;
+ struct bt_hci_cmd_le_set_resolv_enable cmd2;
+ struct bt_hci_cmd_le_set_random_address cmd3;
+ struct bt_hci_cmd_le_set_adv_parameters cmd4;
+ struct bt_hci_cmd_le_set_adv_enable cmd5;
+
+ cmd0.timeout = cpu_to_le16(0x0384);
+
+ bt_hci_send(adv_dev, BT_HCI_CMD_LE_SET_RESOLV_TIMEOUT,
+ &cmd0, sizeof(cmd0), NULL, NULL, NULL);
+
+ cmd1.addr_type = PEER_ADDR_TYPE;
+ memcpy(cmd1.addr, PEER_ADDR, 6);
+ memset(cmd1.peer_irk, 0, 16);
+ memcpy(cmd1.local_irk, ADV_IRK, 16);
+
+ bt_hci_send(adv_dev, BT_HCI_CMD_LE_ADD_TO_RESOLV_LIST,
+ &cmd1, sizeof(cmd1), NULL, NULL, NULL);
+
+ cmd2.enable = 0x01;
+
+ bt_hci_send(adv_dev, BT_HCI_CMD_LE_SET_RESOLV_ENABLE,
+ &cmd2, sizeof(cmd2), NULL, NULL, NULL);
+
+ bt_crypto_random_bytes(crypto, cmd3.addr + 3, 3);
+ cmd3.addr[5] &= 0x3f; /* Clear two most significant bits */
+ cmd3.addr[5] |= 0x40; /* Set second most significant bit */
+ bt_crypto_ah(crypto, cmd1.local_irk, cmd3.addr + 3, cmd3.addr);
+
+ bt_hci_send(adv_dev, BT_HCI_CMD_LE_SET_RANDOM_ADDRESS,
+ &cmd3, sizeof(cmd3), NULL, NULL, NULL);
+
+ printf("Setting advertising address\n");
+ print_rpa(cmd3.addr);
+
+ cmd4.min_interval = cpu_to_le16(0x0800);
+ cmd4.max_interval = cpu_to_le16(0x0800);
+ cmd4.type = 0x03; /* Non-connectable advertising */
+ cmd4.own_addr_type = 0x03; /* Local IRK, random address fallback */
+ cmd4.direct_addr_type = PEER_ADDR_TYPE;
+ memcpy(cmd4.direct_addr, PEER_ADDR, 6);
+ cmd4.channel_map = 0x07;
+ cmd4.filter_policy = 0x00;
+
+ bt_hci_send(adv_dev, BT_HCI_CMD_LE_SET_ADV_PARAMETERS,
+ &cmd4, sizeof(cmd4), NULL, NULL, NULL);
+
+ cmd5.enable = 0x01;
+
+ bt_hci_send(adv_dev, BT_HCI_CMD_LE_SET_ADV_ENABLE,
+ &cmd5, sizeof(cmd5),
+ adv_enable_callback, NULL, NULL);
+}
+
+static void adv_le_features_callback(const void *data, uint8_t size,
+ void *user_data)
+{
+ const struct bt_hci_rsp_le_read_local_features *rsp = data;
+ uint8_t evtmask[] = { 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+ if (rsp->status) {
+ fprintf(stderr, "Failed to read local LE features\n");
+ mainloop_exit_failure();
+ return;
+ }
+
+ bt_hci_send(adv_dev, BT_HCI_CMD_LE_SET_EVENT_MASK, evtmask, 8,
+ adv_le_evtmask_callback, NULL, NULL);
+}
+
+static void adv_features_callback(const void *data, uint8_t size,
+ void *user_data)
+{
+ const struct bt_hci_rsp_read_local_features *rsp = data;
+ uint8_t evtmask[] = { 0x90, 0xe8, 0x04, 0x02, 0x00, 0x80, 0x00, 0x20 };
+
+ if (rsp->status) {
+ fprintf(stderr, "Failed to read local features\n");
+ mainloop_exit_failure();
+ return;
+ }
+
+ if (!(rsp->features[4] & 0x40)) {
+ fprintf(stderr, "Controller without Low Energy support\n");
+ mainloop_exit_failure();
+ return;
+ }
+
+ bt_hci_send(adv_dev, BT_HCI_CMD_SET_EVENT_MASK, evtmask, 8,
+ NULL, NULL, NULL);
+
+ bt_hci_send(adv_dev, BT_HCI_CMD_LE_READ_LOCAL_FEATURES, NULL, 0,
+ adv_le_features_callback, NULL, NULL);
+}
+
+static void scan_le_evtmask_callback(const void *data, uint8_t size,
+ void *user_data)
+{
+ bt_hci_send(adv_dev, BT_HCI_CMD_RESET, NULL, 0, NULL, NULL, NULL);
+
+ bt_hci_send(adv_dev, BT_HCI_CMD_READ_LOCAL_FEATURES, NULL, 0,
+ adv_features_callback, NULL, NULL);
+}
+
+static void scan_le_features_callback(const void *data, uint8_t size,
+ void *user_data)
+{
+ const struct bt_hci_rsp_le_read_local_features *rsp = data;
+ uint8_t evtmask[] = { 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+ if (rsp->status) {
+ fprintf(stderr, "Failed to read local LE features\n");
+ mainloop_exit_failure();
+ return;
+ }
+
+ bt_hci_send(adv_dev, BT_HCI_CMD_LE_SET_EVENT_MASK, evtmask, 8,
+ scan_le_evtmask_callback, NULL, NULL);
+}
+
+static void scan_features_callback(const void *data, uint8_t size,
+ void *user_data)
+{
+ const struct bt_hci_rsp_read_local_features *rsp = data;
+ uint8_t evtmask[] = { 0x90, 0xe8, 0x04, 0x02, 0x00, 0x80, 0x00, 0x20 };
+
+ if (rsp->status) {
+ fprintf(stderr, "Failed to read local features\n");
+ mainloop_exit_failure();
+ return;
+ }
+
+ if (!(rsp->features[4] & 0x40)) {
+ fprintf(stderr, "Controller without Low Energy support\n");
+ mainloop_exit_failure();
+ return;
+ }
+
+ bt_hci_send(scan_dev, BT_HCI_CMD_SET_EVENT_MASK, evtmask, 8,
+ NULL, NULL, NULL);
+
+ bt_hci_send(scan_dev, BT_HCI_CMD_LE_READ_LOCAL_FEATURES, NULL, 0,
+ scan_le_features_callback, NULL, NULL);
+}
+
+static void read_index_list(uint8_t status, uint16_t len, const void *param,
+ void *user_data)
+{
+ const struct mgmt_rp_read_index_list *rp = param;
+ uint16_t count;
+ int i;
+
+ if (status) {
+ fprintf(stderr, "Reading index list failed: %s\n",
+ mgmt_errstr(status));
+ mainloop_exit_failure();
+ return;
+ }
+
+ count = le16_to_cpu(rp->num_controllers);
+
+ if (count < 2) {
+ fprintf(stderr, "At least 2 controllers are required\n");
+ mainloop_exit_failure();
+ return;
+ }
+
+ for (i = 0; i < count; i++) {
+ uint16_t index = cpu_to_le16(rp->index[i]);
+
+ if (index < index1)
+ index1 = index;
+ }
+
+ for (i = 0; i < count; i++) {
+ uint16_t index = cpu_to_le16(rp->index[i]);
+
+ if (index < index2 && index > index1)
+ index2 = index;
+ }
+
+ printf("Selecting index %u for advertiser\n", index1);
+ printf("Selecting index %u for scanner\n", index2);
+
+ crypto = bt_crypto_new();
+ if (!crypto) {
+ fprintf(stderr, "Failed to open crypto interface\n");
+ mainloop_exit_failure();
+ return;
+ }
+
+ adv_dev = bt_hci_new_user_channel(index1);
+ if (!adv_dev) {
+ fprintf(stderr, "Failed to open HCI for advertiser\n");
+ mainloop_exit_failure();
+ return;
+ }
+
+ scan_dev = bt_hci_new_user_channel(index2);
+ if (!scan_dev) {
+ fprintf(stderr, "Failed to open HCI for scanner\n");
+ mainloop_exit_failure();
+ return;
+ }
+
+ bt_hci_register(scan_dev, BT_HCI_EVT_LE_META_EVENT,
+ scan_le_meta_event, NULL, NULL);
+
+ bt_hci_send(scan_dev, BT_HCI_CMD_RESET, NULL, 0, NULL, NULL, NULL);
+
+ bt_hci_send(scan_dev, BT_HCI_CMD_READ_LOCAL_FEATURES, NULL, 0,
+ scan_features_callback, NULL, NULL);
+}
+
+static void signal_callback(int signum, void *user_data)
+{
+ switch (signum) {
+ case SIGINT:
+ case SIGTERM:
+ mainloop_quit();
+ break;
+ }
+}
+
+static void usage(void)
+{
+ printf("advtest - Advertising testing\n"
+ "Usage:\n");
+ printf("\tadvtest [options]\n");
+ printf("options:\n"
+ "\t-h, --help Show help options\n");
+}
+
+static const struct option main_options[] = {
+ { "version", no_argument, NULL, 'v' },
+ { "help", no_argument, NULL, 'h' },
+ { }
+};
+
+int main(int argc ,char *argv[])
+{
+ sigset_t mask;
+ int exit_status;
+
+ for (;;) {
+ int opt;
+
+ opt = getopt_long(argc, argv, "vh", main_options, NULL);
+ if (opt < 0)
+ break;
+
+ switch (opt) {
+ case 'v':
+ printf("%s\n", VERSION);
+ return EXIT_SUCCESS;
+ case 'h':
+ usage();
+ return EXIT_SUCCESS;
+ default:
+ return EXIT_FAILURE;
+ }
+ }
+
+ if (argc - optind > 0) {
+ fprintf(stderr, "Invalid command line parameters\n");
+ return EXIT_FAILURE;
+ }
+
+ mainloop_init();
+
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGINT);
+ sigaddset(&mask, SIGTERM);
+
+ mainloop_set_signal(&mask, signal_callback, NULL, NULL);
+
+ mgmt = mgmt_new_default();
+ if (!mgmt) {
+ fprintf(stderr, "Failed to open management socket\n");
+ return EXIT_FAILURE;
+ }
+
+ if (!mgmt_send(mgmt, MGMT_OP_READ_INDEX_LIST,
+ MGMT_INDEX_NONE, 0, NULL,
+ read_index_list, NULL, NULL)) {
+ fprintf(stderr, "Failed to read index list\n");
+ exit_status = EXIT_FAILURE;
+ goto done;
+ }
+
+ exit_status = mainloop_run();
+
+ bt_hci_unref(adv_dev);
+ bt_hci_unref(scan_dev);
+
+ bt_crypto_unref(crypto);
+
+done:
+ mgmt_unref(mgmt);
+
+ return exit_status;
+}
case 'f':
invalid = 1;
- /* Intentionally missing break */
+ /* fall through */
case 's':
mode = MODE_SEND;
{ 0x0a, "iBT 2.1 (AG620)" },
{ 0x0b, "iBT 3.0 (LnP)" },
{ 0x0c, "iBT 3.0 (WsP)" },
+ { 0x12, "iBT 3.5 (ThP)" },
{ }
};
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
-#include <signal.h>
-#include <sys/signalfd.h>
+#include <string.h>
-#include <readline/readline.h>
-#include <readline/history.h>
#include <glib.h>
#include "gdbus/gdbus.h"
-#include "client/display.h"
+#include "src/shared/shell.h"
/* String display constants */
#define COLORED_NEW COLOR_GREEN "NEW" COLOR_OFF
#define BLUEZ_MEDIA_FOLDER_INTERFACE "org.bluez.MediaFolder1"
#define BLUEZ_MEDIA_ITEM_INTERFACE "org.bluez.MediaItem1"
-static GMainLoop *main_loop;
static DBusConnection *dbus_conn;
static GDBusProxy *default_player;
static GSList *players = NULL;
static void connect_handler(DBusConnection *connection, void *user_data)
{
- rl_set_prompt(PROMPT_ON);
- printf("\r");
- rl_on_new_line();
- rl_redisplay();
+ bt_shell_set_prompt(PROMPT_ON);
}
static void disconnect_handler(DBusConnection *connection, void *user_data)
{
- rl_set_prompt(PROMPT_OFF);
- printf("\r");
- rl_on_new_line();
- rl_redisplay();
-}
-
-static void cmd_quit(int argc, char *argv[])
-{
- g_main_loop_quit(main_loop);
+ bt_shell_set_prompt(PROMPT_OFF);
}
static bool check_default_player(void)
{
if (!default_player) {
- rl_printf("No default player available\n");
+ bt_shell_printf("No default player available\n");
return FALSE;
}
dbus_error_init(&error);
if (dbus_set_error_from_message(&error, message) == TRUE) {
- rl_printf("Failed to play: %s\n", error.name);
+ bt_shell_printf("Failed to play: %s\n", error.name);
dbus_error_free(&error);
return;
}
- rl_printf("Play successful\n");
+ bt_shell_printf("Play successful\n");
}
static GDBusProxy *find_item(const char *path)
proxy = find_item(argv[1]);
if (proxy == NULL) {
- rl_printf("Item %s not available\n", argv[1]);
+ bt_shell_printf("Item %s not available\n", argv[1]);
return;
}
if (g_dbus_proxy_method_call(proxy, "Play", NULL, play_reply,
NULL, NULL) == FALSE) {
- rl_printf("Failed to play\n");
+ bt_shell_printf("Failed to play\n");
return;
}
- rl_printf("Attempting to play %s\n", argv[1]);
+ bt_shell_printf("Attempting to play %s\n", argv[1]);
}
static void cmd_play(int argc, char *argv[])
if (g_dbus_proxy_method_call(default_player, "Play", NULL, play_reply,
NULL, NULL) == FALSE) {
- rl_printf("Failed to play\n");
+ bt_shell_printf("Failed to play\n");
return;
}
- rl_printf("Attempting to play\n");
+ bt_shell_printf("Attempting to play\n");
}
static void pause_reply(DBusMessage *message, void *user_data)
dbus_error_init(&error);
if (dbus_set_error_from_message(&error, message) == TRUE) {
- rl_printf("Failed to pause: %s\n", error.name);
+ bt_shell_printf("Failed to pause: %s\n", error.name);
dbus_error_free(&error);
return;
}
- rl_printf("Pause successful\n");
+ bt_shell_printf("Pause successful\n");
}
static void cmd_pause(int argc, char *argv[])
if (g_dbus_proxy_method_call(default_player, "Pause", NULL,
pause_reply, NULL, NULL) == FALSE) {
- rl_printf("Failed to play\n");
+ bt_shell_printf("Failed to play\n");
return;
}
- rl_printf("Attempting to pause\n");
+ bt_shell_printf("Attempting to pause\n");
}
static void stop_reply(DBusMessage *message, void *user_data)
dbus_error_init(&error);
if (dbus_set_error_from_message(&error, message) == TRUE) {
- rl_printf("Failed to stop: %s\n", error.name);
+ bt_shell_printf("Failed to stop: %s\n", error.name);
dbus_error_free(&error);
return;
}
- rl_printf("Stop successful\n");
+ bt_shell_printf("Stop successful\n");
}
static void cmd_stop(int argc, char *argv[])
if (g_dbus_proxy_method_call(default_player, "Stop", NULL, stop_reply,
NULL, NULL) == FALSE) {
- rl_printf("Failed to stop\n");
+ bt_shell_printf("Failed to stop\n");
return;
}
- rl_printf("Attempting to stop\n");
+ bt_shell_printf("Attempting to stop\n");
}
static void next_reply(DBusMessage *message, void *user_data)
dbus_error_init(&error);
if (dbus_set_error_from_message(&error, message) == TRUE) {
- rl_printf("Failed to jump to next: %s\n", error.name);
+ bt_shell_printf("Failed to jump to next: %s\n", error.name);
dbus_error_free(&error);
return;
}
- rl_printf("Next successful\n");
+ bt_shell_printf("Next successful\n");
}
static void cmd_next(int argc, char *argv[])
if (g_dbus_proxy_method_call(default_player, "Next", NULL, next_reply,
NULL, NULL) == FALSE) {
- rl_printf("Failed to jump to next\n");
+ bt_shell_printf("Failed to jump to next\n");
return;
}
- rl_printf("Attempting to jump to next\n");
+ bt_shell_printf("Attempting to jump to next\n");
}
static void previous_reply(DBusMessage *message, void *user_data)
dbus_error_init(&error);
if (dbus_set_error_from_message(&error, message) == TRUE) {
- rl_printf("Failed to jump to previous: %s\n", error.name);
+ bt_shell_printf("Failed to jump to previous: %s\n", error.name);
dbus_error_free(&error);
return;
}
- rl_printf("Previous successful\n");
+ bt_shell_printf("Previous successful\n");
}
static void cmd_previous(int argc, char *argv[])
if (g_dbus_proxy_method_call(default_player, "Previous", NULL,
previous_reply, NULL, NULL) == FALSE) {
- rl_printf("Failed to jump to previous\n");
+ bt_shell_printf("Failed to jump to previous\n");
return;
}
- rl_printf("Attempting to jump to previous\n");
+ bt_shell_printf("Attempting to jump to previous\n");
}
static void fast_forward_reply(DBusMessage *message, void *user_data)
dbus_error_init(&error);
if (dbus_set_error_from_message(&error, message) == TRUE) {
- rl_printf("Failed to fast forward: %s\n", error.name);
+ bt_shell_printf("Failed to fast forward: %s\n", error.name);
dbus_error_free(&error);
return;
}
- rl_printf("FastForward successful\n");
+ bt_shell_printf("FastForward successful\n");
}
static void cmd_fast_forward(int argc, char *argv[])
if (g_dbus_proxy_method_call(default_player, "FastForward", NULL,
fast_forward_reply, NULL, NULL) == FALSE) {
- rl_printf("Failed to jump to previous\n");
+ bt_shell_printf("Failed to jump to previous\n");
return;
}
- rl_printf("Fast forward playback\n");
+ bt_shell_printf("Fast forward playback\n");
}
static void rewind_reply(DBusMessage *message, void *user_data)
dbus_error_init(&error);
if (dbus_set_error_from_message(&error, message) == TRUE) {
- rl_printf("Failed to rewind: %s\n", error.name);
+ bt_shell_printf("Failed to rewind: %s\n", error.name);
dbus_error_free(&error);
return;
}
- rl_printf("Rewind successful\n");
+ bt_shell_printf("Rewind successful\n");
}
static void cmd_rewind(int argc, char *argv[])
if (g_dbus_proxy_method_call(default_player, "Rewind", NULL,
rewind_reply, NULL, NULL) == FALSE) {
- rl_printf("Failed to rewind\n");
+ bt_shell_printf("Failed to rewind\n");
return;
}
- rl_printf("Rewind playback\n");
+ bt_shell_printf("Rewind playback\n");
}
static void generic_callback(const DBusError *error, void *user_data)
char *str = user_data;
if (dbus_error_is_set(error))
- rl_printf("Failed to set %s: %s\n", str, error->name);
+ bt_shell_printf("Failed to set %s: %s\n", str, error->name);
else
- rl_printf("Changing %s succeeded\n", str);
+ bt_shell_printf("Changing %s succeeded\n", str);
}
static void cmd_equalizer(int argc, char *argv[])
if (!check_default_player())
return;
- if (argc < 2) {
- rl_printf("Missing on/off argument\n");
- return;
- }
-
if (!g_dbus_proxy_get_property(default_player, "Equalizer", &iter)) {
- rl_printf("Operation not supported\n");
+ bt_shell_printf("Operation not supported\n");
return;
}
DBUS_TYPE_STRING, &value,
generic_callback, value,
g_free) == FALSE) {
- rl_printf("Failed to setting equalizer\n");
+ bt_shell_printf("Failed to setting equalizer\n");
g_free(value);
return;
}
- rl_printf("Attempting to set equalizer\n");
+ bt_shell_printf("Attempting to set equalizer\n");
}
static void cmd_repeat(int argc, char *argv[])
if (!check_default_player())
return;
- if (argc < 2) {
- rl_printf("Missing mode argument\n");
- return;
- }
if (!g_dbus_proxy_get_property(default_player, "Repeat", &iter)) {
- rl_printf("Operation not supported\n");
+ bt_shell_printf("Operation not supported\n");
return;
}
DBUS_TYPE_STRING, &value,
generic_callback, value,
g_free) == FALSE) {
- rl_printf("Failed to set repeat\n");
+ bt_shell_printf("Failed to set repeat\n");
g_free(value);
return;
}
- rl_printf("Attempting to set repeat\n");
+ bt_shell_printf("Attempting to set repeat\n");
}
static void cmd_shuffle(int argc, char *argv[])
if (!check_default_player())
return;
- if (argc < 2) {
- rl_printf("Missing mode argument\n");
- return;
- }
-
if (!g_dbus_proxy_get_property(default_player, "Shuffle", &iter)) {
- rl_printf("Operation not supported\n");
+ bt_shell_printf("Operation not supported\n");
return;
}
DBUS_TYPE_STRING, &value,
generic_callback, value,
g_free) == FALSE) {
- rl_printf("Failed to set shuffle\n");
+ bt_shell_printf("Failed to set shuffle\n");
g_free(value);
return;
}
- rl_printf("Attempting to set shuffle\n");
+ bt_shell_printf("Attempting to set shuffle\n");
}
static void cmd_scan(int argc, char *argv[])
if (!check_default_player())
return;
- if (argc < 2) {
- rl_printf("Missing mode argument\n");
- return;
- }
-
if (!g_dbus_proxy_get_property(default_player, "Shuffle", &iter)) {
- rl_printf("Operation not supported\n");
+ bt_shell_printf("Operation not supported\n");
return;
}
DBUS_TYPE_STRING, &value,
generic_callback, value,
g_free) == FALSE) {
- rl_printf("Failed to set scan\n");
+ bt_shell_printf("Failed to set scan\n");
g_free(value);
return;
}
- rl_printf("Attempting to set scan\n");
+ bt_shell_printf("Attempting to set scan\n");
}
static char *proxy_description(GDBusProxy *proxy, const char *title,
str = proxy_description(proxy, "Player", description);
- rl_printf("%s%s\n", str, default_player == proxy ? "[default]" : "");
+ bt_shell_printf("%s%s\n", str, default_player == proxy ? "[default]" : "");
g_free(str);
}
DBusMessageIter subiter;
if (iter == NULL) {
- rl_printf("%s%s is nil\n", label, name);
+ bt_shell_printf("%s%s is nil\n", label, name);
return;
}
switch (dbus_message_iter_get_arg_type(iter)) {
case DBUS_TYPE_INVALID:
- rl_printf("%s%s is invalid\n", label, name);
+ bt_shell_printf("%s%s is invalid\n", label, name);
break;
case DBUS_TYPE_STRING:
case DBUS_TYPE_OBJECT_PATH:
dbus_message_iter_get_basic(iter, &valstr);
- rl_printf("%s%s: %s\n", label, name, valstr);
+ bt_shell_printf("%s%s: %s\n", label, name, valstr);
break;
case DBUS_TYPE_BOOLEAN:
dbus_message_iter_get_basic(iter, &valbool);
- rl_printf("%s%s: %s\n", label, name,
+ bt_shell_printf("%s%s: %s\n", label, name,
valbool == TRUE ? "yes" : "no");
break;
case DBUS_TYPE_UINT32:
dbus_message_iter_get_basic(iter, &valu32);
- rl_printf("%s%s: 0x%06x\n", label, name, valu32);
+ bt_shell_printf("%s%s: 0x%06x\n", label, name, valu32);
break;
case DBUS_TYPE_UINT16:
dbus_message_iter_get_basic(iter, &valu16);
- rl_printf("%s%s: 0x%04x\n", label, name, valu16);
+ bt_shell_printf("%s%s: 0x%04x\n", label, name, valu16);
break;
case DBUS_TYPE_INT16:
dbus_message_iter_get_basic(iter, &vals16);
- rl_printf("%s%s: %d\n", label, name, vals16);
+ bt_shell_printf("%s%s: %d\n", label, name, vals16);
break;
case DBUS_TYPE_VARIANT:
dbus_message_iter_recurse(iter, &subiter);
print_iter(label, valstr, &subiter);
break;
default:
- rl_printf("%s%s has unsupported type\n", label, name);
+ bt_shell_printf("%s%s has unsupported type\n", label, name);
break;
}
}
{
GDBusProxy *proxy;
- if (argc < 2) {
- rl_printf("Missing item address argument\n");
- return;
- }
-
proxy = find_item(argv[1]);
if (!proxy) {
- rl_printf("Item %s not available\n", argv[1]);
+ bt_shell_printf("Item %s not available\n", argv[1]);
return;
}
- rl_printf("Item %s\n", g_dbus_proxy_get_path(proxy));
+ bt_shell_printf("Item %s\n", g_dbus_proxy_get_path(proxy));
print_property(proxy, "Player");
print_property(proxy, "Name");
} else {
proxy = find_player(argv[1]);
if (!proxy) {
- rl_printf("Player %s not available\n", argv[1]);
+ bt_shell_printf("Player %s not available\n", argv[1]);
return;
}
}
- rl_printf("Player %s\n", g_dbus_proxy_get_path(proxy));
+ bt_shell_printf("Player %s\n", g_dbus_proxy_get_path(proxy));
print_property(proxy, "Name");
print_property(proxy, "Repeat");
if (folder == NULL)
return;
- rl_printf("Folder %s\n", g_dbus_proxy_get_path(proxy));
+ bt_shell_printf("Folder %s\n", g_dbus_proxy_get_path(proxy));
print_property(folder, "Name");
print_property(folder, "NumberOfItems");
if (item == NULL)
return;
- rl_printf("Playlist %s\n", path);
+ bt_shell_printf("Playlist %s\n", path);
print_property(item, "Name");
}
{
GDBusProxy *proxy;
- if (argc < 2) {
- rl_printf("Missing player address argument\n");
- return;
- }
-
proxy = find_player(argv[1]);
if (proxy == NULL) {
- rl_printf("Player %s not available\n", argv[1]);
+ bt_shell_printf("Player %s not available\n", argv[1]);
return;
}
dbus_error_init(&error);
if (dbus_set_error_from_message(&error, message) == TRUE) {
- rl_printf("Failed to change folder: %s\n", error.name);
+ bt_shell_printf("Failed to change folder: %s\n", error.name);
dbus_error_free(&error);
return;
}
- rl_printf("ChangeFolder successful\n");
+ bt_shell_printf("ChangeFolder successful\n");
}
static void change_folder_setup(DBusMessageIter *iter, void *user_data)
{
GDBusProxy *proxy;
- if (argc < 2) {
- rl_printf("Missing item argument\n");
- return;
- }
-
if (dbus_validate_path(argv[1], NULL) == FALSE) {
- rl_printf("Not a valid path\n");
+ bt_shell_printf("Not a valid path\n");
return;
}
proxy = find_folder(g_dbus_proxy_get_path(default_player));
if (proxy == NULL) {
- rl_printf("Operation not supported\n");
+ bt_shell_printf("Operation not supported\n");
return;
}
if (g_dbus_proxy_method_call(proxy, "ChangeFolder", change_folder_setup,
change_folder_reply, argv[1], NULL) == FALSE) {
- rl_printf("Failed to change current folder\n");
+ bt_shell_printf("Failed to change current folder\n");
return;
}
- rl_printf("Attempting to change folder\n");
+ bt_shell_printf("Attempting to change folder\n");
}
static void append_variant(DBusMessageIter *iter, int type, void *val)
dbus_error_init(&error);
if (dbus_set_error_from_message(&error, message) == TRUE) {
- rl_printf("Failed to list items: %s\n", error.name);
+ bt_shell_printf("Failed to list items: %s\n", error.name);
dbus_error_free(&error);
return;
}
- rl_printf("ListItems successful\n");
+ bt_shell_printf("ListItems successful\n");
}
static void cmd_list_items(int argc, char *argv[])
proxy = find_folder(g_dbus_proxy_get_path(default_player));
if (proxy == NULL) {
- rl_printf("Operation not supported\n");
+ bt_shell_printf("Operation not supported\n");
return;
}
errno = 0;
args->start = strtol(argv[1], NULL, 10);
if (errno != 0) {
- rl_printf("%s(%d)\n", strerror(errno), errno);
+ bt_shell_printf("%s(%d)\n", strerror(errno), errno);
g_free(args);
return;
}
errno = 0;
args->end = strtol(argv[2], NULL, 10);
if (errno != 0) {
- rl_printf("%s(%d)\n", strerror(errno), errno);
+ bt_shell_printf("%s(%d)\n", strerror(errno), errno);
g_free(args);
return;
}
done:
if (g_dbus_proxy_method_call(proxy, "ListItems", list_items_setup,
list_items_reply, args, g_free) == FALSE) {
- rl_printf("Failed to change current folder\n");
+ bt_shell_printf("Failed to change current folder\n");
g_free(args);
return;
}
- rl_printf("Attempting to list items\n");
+ bt_shell_printf("Attempting to list items\n");
}
static void search_setup(DBusMessageIter *iter, void *user_data)
dbus_error_init(&error);
if (dbus_set_error_from_message(&error, message) == TRUE) {
- rl_printf("Failed to search: %s\n", error.name);
+ bt_shell_printf("Failed to search: %s\n", error.name);
dbus_error_free(&error);
return;
}
- rl_printf("Search successful\n");
+ bt_shell_printf("Search successful\n");
}
static void cmd_search(int argc, char *argv[])
GDBusProxy *proxy;
char *string;
- if (argc < 2) {
- rl_printf("Missing string argument\n");
- return;
- }
-
if (check_default_player() == FALSE)
return;
proxy = find_folder(g_dbus_proxy_get_path(default_player));
if (proxy == NULL) {
- rl_printf("Operation not supported\n");
+ bt_shell_printf("Operation not supported\n");
return;
}
if (g_dbus_proxy_method_call(proxy, "Search", search_setup,
search_reply, string, g_free) == FALSE) {
- rl_printf("Failed to search\n");
+ bt_shell_printf("Failed to search\n");
g_free(string);
return;
}
- rl_printf("Attempting to search\n");
+ bt_shell_printf("Attempting to search\n");
}
static void add_to_nowplaying_reply(DBusMessage *message, void *user_data)
dbus_error_init(&error);
if (dbus_set_error_from_message(&error, message) == TRUE) {
- rl_printf("Failed to queue: %s\n", error.name);
+ bt_shell_printf("Failed to queue: %s\n", error.name);
dbus_error_free(&error);
return;
}
- rl_printf("AddToNowPlaying successful\n");
+ bt_shell_printf("AddToNowPlaying successful\n");
}
static void cmd_queue(int argc, char *argv[])
{
GDBusProxy *proxy;
- if (argc < 2) {
- rl_printf("Missing item address argument\n");
- return;
- }
-
proxy = find_item(argv[1]);
if (proxy == NULL) {
- rl_printf("Item %s not available\n", argv[1]);
+ bt_shell_printf("Item %s not available\n", argv[1]);
return;
}
if (g_dbus_proxy_method_call(proxy, "AddtoNowPlaying", NULL,
add_to_nowplaying_reply, NULL,
NULL) == FALSE) {
- rl_printf("Failed to play\n");
+ bt_shell_printf("Failed to play\n");
return;
}
- rl_printf("Attempting to queue %s\n", argv[1]);
+ bt_shell_printf("Attempting to queue %s\n", argv[1]);
}
-static const struct {
- const char *cmd;
- const char *arg;
- void (*func) (int argc, char *argv[]);
- const char *desc;
-} cmd_table[] = {
+static const struct bt_shell_menu main_menu = {
+ .name = "main",
+ .entries = {
{ "list", NULL, cmd_list, "List available players" },
{ "show", "[player]", cmd_show, "Player information" },
{ "select", "<player>", cmd_select, "Select default player" },
"Change current folder" },
{ "list-items", "[start] [end]", cmd_list_items,
"List items of current folder" },
- { "search", "string", cmd_search,
+ { "search", "<string>", cmd_search,
"Search items containing string" },
{ "queue", "<item>", cmd_queue, "Add item to playlist queue" },
{ "show-item", "<item>", cmd_show_item, "Show item information" },
- { "quit", NULL, cmd_quit, "Quit program" },
- { "exit", NULL, cmd_quit },
- { "help" },
- {}
-};
-
-static char *cmd_generator(const char *text, int state)
-{
- static int index, len;
- const char *cmd;
-
- if (!state) {
- index = 0;
- len = strlen(text);
- }
-
- while ((cmd = cmd_table[index].cmd)) {
- index++;
-
- if (!strncmp(cmd, text, len))
- return strdup(cmd);
- }
-
- return NULL;
-}
-
-static char **cmd_completion(const char *text, int start, int end)
-{
- char **matches = NULL;
-
- if (start == 0) {
- rl_completion_display_matches_hook = NULL;
- matches = rl_completion_matches(text, cmd_generator);
- }
-
- if (!matches)
- rl_attempted_completion_over = 1;
-
- return matches;
-}
-
-static void rl_handler(char *input)
-{
- int argc;
- char **argv = NULL;
- int i;
-
- if (!input) {
- rl_insert_text("quit");
- rl_redisplay();
- rl_crlf();
- g_main_loop_quit(main_loop);
- return;
- }
-
- if (!strlen(input))
- goto done;
-
- g_strstrip(input);
- add_history(input);
-
- argv = g_strsplit(input, " ", -1);
- if (argv == NULL)
- goto done;
-
- for (argc = 0; argv[argc];)
- argc++;
-
- if (argc == 0)
- goto done;
-
- for (i = 0; cmd_table[i].cmd; i++) {
- if (strcmp(argv[0], cmd_table[i].cmd))
- continue;
-
- if (cmd_table[i].func) {
- cmd_table[i].func(argc, argv);
- goto done;
- }
- }
-
- if (strcmp(argv[0], "help")) {
- printf("Invalid command\n");
- goto done;
- }
-
- printf("Available commands:\n");
-
- for (i = 0; cmd_table[i].cmd; i++) {
- if (cmd_table[i].desc)
- printf("\t%s %s\t%s\n", cmd_table[i].cmd,
- cmd_table[i].arg ? : " ",
- cmd_table[i].desc);
- }
-
-done:
- g_strfreev(argv);
- free(input);
-}
-
-static gboolean option_version = FALSE;
-
-static GOptionEntry options[] = {
- { "version", 'v', 0, G_OPTION_ARG_NONE, &option_version,
- "Show version information and exit" },
- { NULL },
+ {} },
};
-static gboolean signal_handler(GIOChannel *channel, GIOCondition condition,
- gpointer user_data)
-{
- static unsigned int __terminated = 0;
- struct signalfd_siginfo si;
- ssize_t result;
- int fd;
-
- if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
- g_main_loop_quit(main_loop);
- return FALSE;
- }
-
- fd = g_io_channel_unix_get_fd(channel);
-
- result = read(fd, &si, sizeof(si));
- if (result != sizeof(si))
- return FALSE;
-
- switch (si.ssi_signo) {
- case SIGINT:
- rl_replace_line("", 0);
- rl_crlf();
- rl_on_new_line();
- rl_redisplay();
- break;
- case SIGTERM:
- if (__terminated == 0) {
- rl_replace_line("", 0);
- rl_crlf();
- g_main_loop_quit(main_loop);
- }
-
- __terminated = 1;
- break;
- }
-
- return TRUE;
-}
-
-static guint setup_signalfd(void)
-{
- GIOChannel *channel;
- guint source;
- sigset_t mask;
- int fd;
-
- sigemptyset(&mask);
- sigaddset(&mask, SIGINT);
- sigaddset(&mask, SIGTERM);
-
- if (sigprocmask(SIG_BLOCK, &mask, NULL) < 0) {
- perror("Failed to set signal mask");
- return 0;
- }
-
- fd = signalfd(-1, &mask, 0);
- if (fd < 0) {
- perror("Failed to create signal descriptor");
- return 0;
- }
-
- channel = g_io_channel_unix_new(fd);
-
- g_io_channel_set_close_on_unref(channel, TRUE);
- g_io_channel_set_encoding(channel, NULL, NULL);
- g_io_channel_set_buffered(channel, FALSE);
-
- source = g_io_add_watch(channel,
- G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
- signal_handler, NULL);
-
- g_io_channel_unref(channel);
-
- return source;
-}
-
-static gboolean input_handler(GIOChannel *channel, GIOCondition condition,
- gpointer user_data)
-{
- if (condition & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) {
- g_main_loop_quit(main_loop);
- return FALSE;
- }
-
- rl_callback_read_char();
- return TRUE;
-}
-
-static guint setup_standard_input(void)
-{
- GIOChannel *channel;
- guint source;
-
- channel = g_io_channel_unix_new(fileno(stdin));
-
- source = g_io_add_watch(channel,
- G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
- input_handler, NULL);
-
- g_io_channel_unref(channel);
-
- return source;
-}
-
static void player_added(GDBusProxy *proxy)
{
players = g_slist_append(players, proxy);
path = g_dbus_proxy_get_path(proxy);
- rl_printf("%s%s%sFolder %s\n", description ? "[" : "",
+ bt_shell_printf("%s%s%sFolder %s\n", description ? "[" : "",
description ? : "",
description ? "] " : "",
path);
else
name = "<unknown>";
- rl_printf("%s%s%sItem %s %s\n", description ? "[" : "",
+ bt_shell_printf("%s%s%sItem %s %s\n", description ? "[" : "",
description ? : "",
description ? "] " : "",
path, name);
int main(int argc, char *argv[])
{
- GOptionContext *context;
- GError *error = NULL;
GDBusClient *client;
- guint signal, input;
-
- context = g_option_context_new(NULL);
- g_option_context_add_main_entries(context, options, NULL);
-
- if (g_option_context_parse(context, &argc, &argv, &error) == FALSE) {
- if (error != NULL) {
- g_printerr("%s\n", error->message);
- g_error_free(error);
- } else
- g_printerr("An unknown error occurred\n");
- exit(1);
- }
- g_option_context_free(context);
-
- if (option_version == TRUE) {
- printf("%s\n", VERSION);
- exit(0);
- }
+ bt_shell_init(argc, argv, NULL);
+ bt_shell_set_menu(&main_menu);
+ bt_shell_set_prompt(PROMPT_OFF);
+ bt_shell_attach(fileno(stdin));
- main_loop = g_main_loop_new(NULL, FALSE);
dbus_conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, NULL);
- rl_attempted_completion_function = cmd_completion;
-
- rl_erase_empty_line = 1;
- rl_callback_handler_install(NULL, rl_handler);
-
- rl_set_prompt(PROMPT_OFF);
- rl_redisplay();
-
- input = setup_standard_input();
- signal = setup_signalfd();
client = g_dbus_client_new(dbus_conn, "org.bluez", "/org/bluez");
g_dbus_client_set_connect_watch(client, connect_handler, NULL);
g_dbus_client_set_proxy_handlers(client, proxy_added, proxy_removed,
property_changed, NULL);
- g_main_loop_run(main_loop);
+ bt_shell_run();
g_dbus_client_unref(client);
- g_source_remove(signal);
- g_source_remove(input);
-
- rl_message("");
- rl_callback_handler_remove();
dbus_connection_unref(dbus_conn);
- g_main_loop_unref(main_loop);
return 0;
}
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011-2012 Intel Corporation
+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <getopt.h>
+#include <stdbool.h>
+
+#include "src/shared/mainloop.h"
+#include "src/shared/util.h"
+#include "src/shared/mgmt.h"
+
+static struct mgmt *mgmt = NULL;
+
+static void mgmt_debug(const char *str, void *user_data)
+{
+ const char *prefix = user_data;
+
+ printf("%s%s\n", prefix, str);
+}
+
+static void signal_callback(int signum, void *user_data)
+{
+ static bool terminated = false;
+
+ switch (signum) {
+ case SIGINT:
+ case SIGTERM:
+ if (!terminated) {
+ mainloop_quit();
+ terminated = true;
+ }
+ break;
+ }
+}
+static void usage(void)
+{
+ printf("btconfig - Bluetooth configuration utility\n"
+ "Usage:\n");
+ printf("\tbtconfig [options]\n");
+ printf("options:\n"
+ "\t-h, --help Show help options\n");
+}
+
+static const struct option main_options[] = {
+ { "version", no_argument, NULL, 'v' },
+ { "help", no_argument, NULL, 'h' },
+ { }
+};
+
+int main(int argc, char *argv[])
+{
+ sigset_t mask;
+ int exit_status;
+
+ for (;;) {
+ int opt;
+
+ opt = getopt_long(argc, argv, "vh",
+ main_options, NULL);
+ if (opt < 0)
+ break;
+
+ switch (opt) {
+ case 'v':
+ printf("%s\n", VERSION);
+ return EXIT_SUCCESS;
+ case 'h':
+ usage();
+ return EXIT_SUCCESS;
+ default:
+ return EXIT_FAILURE;
+ }
+ }
+
+ if (argc - optind > 0) {
+ fprintf(stderr, "Invalid command line parameters\n");
+ return EXIT_FAILURE;
+ }
+
+ mainloop_init();
+
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGINT);
+ sigaddset(&mask, SIGTERM);
+
+ mainloop_set_signal(&mask, signal_callback, NULL, NULL);
+
+ mgmt = mgmt_new_default();
+ if (!mgmt) {
+ fprintf(stderr, "Unable to open mgmt_socket\n");
+ return EXIT_FAILURE;
+ }
+
+ if (getenv("MGMT_DEBUG"))
+ mgmt_set_debug(mgmt, mgmt_debug, "mgmt: ", NULL);
+
+ exit_status = mainloop_run();
+
+ mgmt_cancel_all(mgmt);
+ mgmt_unregister_all(mgmt);
+
+ mgmt_unref(mgmt);
+
+ return exit_status;
+}
NULL);
}
- bt_gatt_client_set_ready_handler(cli->gatt, ready_cb, cli, NULL);
+ bt_gatt_client_ready_register(cli->gatt, ready_cb, cli, NULL);
bt_gatt_client_set_service_changed(cli->gatt, service_changed_cb, cli,
NULL);
return;
}
- PRLOG("Registered notify handler!");
+ PRLOG("Registered notify handler!\n");
}
static void cmd_register_notify(struct client *cli, char *cmd_str)
return;
}
- PRLOG("Registering notify handler with id: %u\n", id);
+ printf("Registering notify handler with id: %u\n", id);
}
static void unregister_notify_usage(void)
static void set_security_usage(void)
{
- printf("Usage: set_security <level>\n"
+ printf("Usage: set-security <level>\n"
"level: 1-3\n"
"e.g.:\n"
- "\tset-sec-level 2\n");
+ "\tset-security 2\n");
}
static void cmd_set_security(struct client *cli, char *cmd_str)
{
- char *argvbuf[1];
- char **argv = argvbuf;
+ char *argv[2];
int argc = 0;
char *endptr = NULL;
int level;
#define PROMPT_ON COLOR_BLUE "[mgmt]" COLOR_OFF "# "
+static void set_index(char *arg)
+{
+ if (!arg || !strcmp(arg, "none") || !strcmp(arg, "any") ||
+ !strcmp(arg, "all"))
+ mgmt_index = MGMT_INDEX_NONE;
+ else if(strlen(arg) > 3 && !strncasecmp(arg, "hci", 3))
+ mgmt_index = atoi(&arg[3]);
+ else
+ mgmt_index = atoi(arg);
+}
+
static void update_prompt(uint16_t index)
{
char str[32];
noninteractive_quit(EXIT_SUCCESS);
}
+static void cmd_usage(char *cmd);
+
static void cmd_version(struct mgmt *mgmt, uint16_t index, int argc,
char **argv)
{
noninteractive_quit(EXIT_SUCCESS);
}
-static void cmd_setting(struct mgmt *mgmt, uint16_t index, uint16_t op,
- int argc, char **argv)
+static bool parse_setting(int argc, char **argv, uint8_t *val)
{
- uint8_t val;
-
if (argc < 2) {
- print("Specify \"on\" or \"off\"");
- return noninteractive_quit(EXIT_FAILURE);
+ cmd_usage(argv[0]);
+ return false;
}
if (strcasecmp(argv[1], "on") == 0 || strcasecmp(argv[1], "yes") == 0)
- val = 1;
+ *val = 1;
else if (strcasecmp(argv[1], "off") == 0)
- val = 0;
+ *val = 0;
else
- val = atoi(argv[1]);
+ *val = atoi(argv[1]);
+ return true;
+}
+
+static void cmd_setting(struct mgmt *mgmt, uint16_t index, uint16_t op,
+ int argc, char **argv)
+{
+ uint8_t val;
+
+ if (parse_setting(argc, argv, &val) == false)
+ return noninteractive_quit(EXIT_FAILURE);
if (index == MGMT_INDEX_NONE)
index = 0;
struct mgmt_cp_set_discoverable cp;
if (argc < 2) {
- print("Usage: %s <yes/no/limited> [timeout]", argv[0]);
+ cmd_usage(argv[0]);
return noninteractive_quit(EXIT_FAILURE);
}
uint8_t val;
if (argc < 2) {
- print("Specify \"on\" or \"off\" or \"only\"");
+ cmd_usage(argv[0]);
return noninteractive_quit(EXIT_FAILURE);
}
{
struct mgmt_cp_set_privacy cp;
- if (argc < 2) {
- print("Specify \"on\" or \"off\"");
+ if (parse_setting(argc, argv, &cp.privacy) == false)
return noninteractive_quit(EXIT_FAILURE);
- }
-
- if (strcasecmp(argv[1], "on") == 0 || strcasecmp(argv[1], "yes") == 0)
- cp.privacy = 0x01;
- else if (strcasecmp(argv[1], "off") == 0)
- cp.privacy = 0x00;
- else
- cp.privacy = atoi(argv[1]);
if (index == MGMT_INDEX_NONE)
index = 0;
uint8_t class[2];
if (argc < 3) {
- print("Usage: %s <major> <minor>", argv[0]);
+ cmd_usage(argv[0]);
return noninteractive_quit(EXIT_FAILURE);
}
noninteractive_quit(EXIT_SUCCESS);
}
-static void disconnect_usage(void)
-{
- print("Usage: disconnect [-t type] <remote address>");
-}
-
static struct option disconnect_options[] = {
{ "help", 0, 0, 'h' },
{ "type", 1, 0, 't' },
struct mgmt_cp_disconnect cp;
uint8_t type = BDADDR_BREDR;
int opt;
+ char *cmd = argv[0];
while ((opt = getopt_long(argc, argv, "+t:h", disconnect_options,
NULL)) != -1) {
type = strtol(optarg, NULL, 0);
break;
case 'h':
- disconnect_usage();
+ cmd_usage(cmd);
optind = 0;
return noninteractive_quit(EXIT_SUCCESS);
default:
- disconnect_usage();
+ cmd_usage(cmd);
optind = 0;
return noninteractive_quit(EXIT_FAILURE);
}
optind = 0;
if (argc < 1) {
- disconnect_usage();
+ cmd_usage(cmd);
return noninteractive_quit(EXIT_FAILURE);
}
discovery = true;
}
-static void find_service_usage(void)
-{
- print("Usage: find-service [-u UUID] [-r RSSI_Threshold] [-l|-b]");
-}
-
static struct option find_service_options[] = {
{ "help", no_argument, 0, 'h' },
{ "le-only", no_argument, 0, 'l' },
int8_t rssi;
uint16_t count;
int opt;
+ char *cmd = argv[0];
if (index == MGMT_INDEX_NONE)
index = 0;
count = 0;
if (argc == 1) {
- find_service_usage();
+ cmd_usage(cmd);
return noninteractive_quit(EXIT_FAILURE);
}
- while ((opt = getopt_long(argc, argv, "+lbu:r:p:h",
+ while ((opt = getopt_long(argc, argv, "+lbu:r:h",
find_service_options, NULL)) != -1) {
switch (opt) {
case 'l':
rssi = atoi(optarg);
break;
case 'h':
- find_service_usage();
+ cmd_usage(cmd);
optind = 0;
return noninteractive_quit(EXIT_SUCCESS);
default:
- find_service_usage();
+ cmd_usage(cmd);
optind = 0;
return noninteractive_quit(EXIT_FAILURE);
}
optind = 0;
if (argc > 0) {
- find_service_usage();
+ cmd_usage(cmd);
return noninteractive_quit(EXIT_FAILURE);
}
discovery = true;
}
-static void find_usage(void)
-{
- print("Usage: find [-l|-b]>");
-}
-
static struct option find_options[] = {
{ "help", 0, 0, 'h' },
{ "le-only", 1, 0, 'l' },
uint8_t op = MGMT_OP_START_DISCOVERY;
uint8_t type = SCAN_TYPE_DUAL;
int opt;
+ char *cmd = argv[0];
if (index == MGMT_INDEX_NONE)
index = 0;
op = MGMT_OP_START_LIMITED_DISCOVERY;
break;
case 'h':
- find_usage();
+ cmd_usage(cmd);
optind = 0;
return noninteractive_quit(EXIT_SUCCESS);
default:
- find_usage();
+ cmd_usage(cmd);
optind = 0;
return noninteractive_quit(EXIT_FAILURE);
}
void *user_data)
{
if (status != 0) {
- fprintf(stderr,
- "Stop Discovery failed: status 0x%02x (%s)\n",
+ error("Stop Discovery failed: status 0x%02x (%s)",
status, mgmt_errstr(status));
return noninteractive_quit(EXIT_SUCCESS);
}
- printf("Discovery stopped\n");
+ print("Discovery stopped");
discovery = false;
noninteractive_quit(EXIT_SUCCESS);
}
-static void stop_find_usage(void)
-{
- printf("Usage: btmgmt stop-find [-l|-b]>\n");
-}
-
static struct option stop_find_options[] = {
{ "help", 0, 0, 'h' },
{ "le-only", 1, 0, 'l' },
struct mgmt_cp_stop_discovery cp;
uint8_t type = SCAN_TYPE_DUAL;
int opt;
+ char *cmd = argv[0];
if (index == MGMT_INDEX_NONE)
index = 0;
break;
case 'h':
default:
- stop_find_usage();
+ cmd_usage(cmd);
optind = 0;
- exit(EXIT_SUCCESS);
+ return noninteractive_quit(EXIT_SUCCESS);
}
}
if (mgmt_send(mgmt, MGMT_OP_STOP_DISCOVERY, index, sizeof(cp), &cp,
stop_find_rsp, NULL, NULL) == 0) {
- fprintf(stderr, "Unable to send stop_discovery cmd\n");
- exit(EXIT_FAILURE);
+ error("Unable to send stop_discovery cmd");
+ return noninteractive_quit(EXIT_FAILURE);
}
}
struct mgmt_cp_set_local_name cp;
if (argc < 2) {
- print("Usage: %s <name> [shortname]", argv[0]);
+ cmd_usage(argv[0]);
return noninteractive_quit(EXIT_FAILURE);
}
noninteractive_quit(EXIT_SUCCESS);
}
-static void pair_usage(void)
-{
- print("Usage: pair [-c cap] [-t type] <remote address>");
-}
-
static struct option pair_options[] = {
{ "help", 0, 0, 'h' },
{ "capability", 1, 0, 'c' },
uint8_t type = BDADDR_BREDR;
char addr[18];
int opt;
+ char *cmd = argv[0];
while ((opt = getopt_long(argc, argv, "+c:t:h", pair_options,
NULL)) != -1) {
type = strtol(optarg, NULL, 0);
break;
case 'h':
- pair_usage();
+ cmd_usage(cmd);
optind = 0;
return noninteractive_quit(EXIT_SUCCESS);
default:
- pair_usage();
+ cmd_usage(cmd);
optind = 0;
return noninteractive_quit(EXIT_FAILURE);
}
optind = 0;
if (argc < 1) {
- pair_usage();
+ cmd_usage(cmd);
return noninteractive_quit(EXIT_FAILURE);
}
noninteractive_quit(EXIT_SUCCESS);
}
-static void cancel_pair_usage(void)
-{
- print("Usage: cancelpair [-t type] <remote address>");
-}
-
static struct option cancel_pair_options[] = {
{ "help", 0, 0, 'h' },
{ "type", 1, 0, 't' },
struct mgmt_addr_info cp;
uint8_t type = BDADDR_BREDR;
int opt;
+ char *cmd = argv[0];
while ((opt = getopt_long(argc, argv, "+t:h", cancel_pair_options,
NULL)) != -1) {
type = strtol(optarg, NULL, 0);
break;
case 'h':
- cancel_pair_usage();
+ cmd_usage(cmd);
optind = 0;
return noninteractive_quit(EXIT_SUCCESS);
default:
- cancel_pair_usage();
+ cmd_usage(cmd);
optind = 0;
return noninteractive_quit(EXIT_FAILURE);
}
optind = 0;
if (argc < 1) {
- cancel_pair_usage();
+ cmd_usage(cmd);
return noninteractive_quit(EXIT_FAILURE);
}
noninteractive_quit(EXIT_SUCCESS);
}
-static void unpair_usage(void)
-{
- print("Usage: unpair [-t type] <remote address>");
-}
-
static struct option unpair_options[] = {
{ "help", 0, 0, 'h' },
{ "type", 1, 0, 't' },
struct mgmt_cp_unpair_device cp;
uint8_t type = BDADDR_BREDR;
int opt;
+ char *cmd = argv[0];
while ((opt = getopt_long(argc, argv, "+t:h", unpair_options,
NULL)) != -1) {
type = strtol(optarg, NULL, 0);
break;
case 'h':
- unpair_usage();
+ cmd_usage(cmd);
optind = 0;
return noninteractive_quit(EXIT_SUCCESS);
default:
- unpair_usage();
+ cmd_usage(cmd);
optind = 0;
return noninteractive_quit(EXIT_FAILURE);
}
optind = 0;
if (argc < 1) {
- unpair_usage();
+ cmd_usage(cmd);
return noninteractive_quit(EXIT_FAILURE);
}
noninteractive_quit(EXIT_SUCCESS);
}
-static void irks_usage(void)
-{
- print("Usage: irks [--local]");
-}
-
static struct option irks_options[] = {
{ "help", 0, 0, 'h' },
{ "local", 1, 0, 'l' },
uint16_t count, local_index;
char path[PATH_MAX];
int opt;
+ char *cmd = argv[0];
if (index == MGMT_INDEX_NONE)
index = 0;
count++;
break;
case 'h':
- irks_usage();
+ cmd_usage(cmd);
optind = 0;
return noninteractive_quit(EXIT_SUCCESS);
default:
- irks_usage();
+ cmd_usage(cmd);
optind = 0;
return noninteractive_quit(EXIT_FAILURE);
}
optind = 0;
if (argc > 0) {
- irks_usage();
+ cmd_usage(cmd);
return noninteractive_quit(EXIT_FAILURE);
}
noninteractive_quit(EXIT_SUCCESS);
}
-static void block_usage(void)
-{
- print("Usage: block [-t type] <remote address>");
-}
-
static struct option block_options[] = {
{ "help", 0, 0, 'h' },
{ "type", 1, 0, 't' },
struct mgmt_cp_block_device cp;
uint8_t type = BDADDR_BREDR;
int opt;
+ char *cmd = argv[0];
while ((opt = getopt_long(argc, argv, "+t:h", block_options,
NULL)) != -1) {
type = strtol(optarg, NULL, 0);
break;
case 'h':
- block_usage();
+ cmd_usage(cmd);
optind = 0;
return noninteractive_quit(EXIT_SUCCESS);
default:
- block_usage();
+ cmd_usage(cmd);
optind = 0;
return noninteractive_quit(EXIT_FAILURE);
}
optind = 0;
if (argc < 1) {
- block_usage();
+ cmd_usage(cmd);
return noninteractive_quit(EXIT_FAILURE);
}
}
}
-static void unblock_usage(void)
-{
- print("Usage: unblock [-t type] <remote address>");
-}
-
static void cmd_unblock(struct mgmt *mgmt, uint16_t index, int argc,
char **argv)
{
struct mgmt_cp_unblock_device cp;
uint8_t type = BDADDR_BREDR;
int opt;
+ char *cmd = argv[0];
while ((opt = getopt_long(argc, argv, "+t:h", block_options,
NULL)) != -1) {
type = strtol(optarg, NULL, 0);
break;
case 'h':
- unblock_usage();
+ cmd_usage(cmd);
optind = 0;
return noninteractive_quit(EXIT_SUCCESS);
default:
- unblock_usage();
+ cmd_usage(cmd);
optind = 0;
return noninteractive_quit(EXIT_FAILURE);
}
optind = 0;
if (argc < 1) {
- unblock_usage();
+ cmd_usage(cmd);
return noninteractive_quit(EXIT_FAILURE);
}
print("Remote OOB data added for %s (%u)", addr, rp->type);
}
-static void remote_oob_usage(void)
-{
- print("Usage: remote-oob [-t <addr_type>] "
- "[-r <rand192>] [-h <hash192>] [-R <rand256>] [-H <hash256>] "
- "<addr>");
-}
-
static struct option remote_oob_opt[] = {
- { "help", 0, 0, 'h' },
+ { "help", 0, 0, '?' },
{ "type", 1, 0, 't' },
{ 0, 0, 0, 0 }
};
{
struct mgmt_cp_add_remote_oob_data cp;
int opt;
+ char *cmd = argv[0];
memset(&cp, 0, sizeof(cp));
cp.addr.type = BDADDR_BREDR;
hex2bin(optarg, cp.hash256, 16);
break;
default:
- remote_oob_usage();
+ cmd_usage(cmd);
+ optind = 0;
return noninteractive_quit(EXIT_FAILURE);
}
}
optind = 0;
if (argc < 1) {
- remote_oob_usage();
+ cmd_usage(cmd);
return noninteractive_quit(EXIT_FAILURE);
}
static void did_usage(void)
{
- print("Usage: did <source>:<vendor>:<product>:<version>");
+ cmd_usage("did");
print(" possible source values: bluetooth, usb");
}
noninteractive_quit(EXIT_SUCCESS);
}
-static void static_addr_usage(void)
-{
- print("Usage: static-addr <address>");
-}
-
static void cmd_static_addr(struct mgmt *mgmt, uint16_t index,
int argc, char **argv)
{
struct mgmt_cp_set_static_address cp;
if (argc < 2) {
- static_addr_usage();
+ cmd_usage(argv[0]);
return noninteractive_quit(EXIT_FAILURE);
}
struct mgmt_cp_set_public_address cp;
if (argc < 2) {
- print("Usage: public-addr <address>");
+ cmd_usage(argv[0]);
return noninteractive_quit(EXIT_FAILURE);
}
{
struct mgmt_cp_set_external_config cp;
- if (argc < 2) {
- print("Specify \"on\" or \"off\"");
+ if (parse_setting(argc, argv, &cp.config) == false)
return noninteractive_quit(EXIT_FAILURE);
- }
-
- if (strcasecmp(argv[1], "on") == 0 || strcasecmp(argv[1], "yes") == 0)
- cp.config = 0x01;
- else if (strcasecmp(argv[1], "off") == 0)
- cp.config = 0x00;
- else
- cp.config = atoi(argv[1]);
if (index == MGMT_INDEX_NONE)
index = 0;
noninteractive_quit(EXIT_SUCCESS);
}
-static void conn_info_usage(void)
-{
- print("Usage: conn-info [-t type] <remote address>");
-}
-
static struct option conn_info_options[] = {
{ "help", 0, 0, 'h' },
{ "type", 1, 0, 't' },
struct mgmt_cp_get_conn_info cp;
uint8_t type = BDADDR_BREDR;
int opt;
+ char *cmd = argv[0];
while ((opt = getopt_long(argc, argv, "+t:h", conn_info_options,
NULL)) != -1) {
type = strtol(optarg, NULL, 0);
break;
case 'h':
- conn_info_usage();
+ cmd_usage(cmd);
+ optind = 0;
return noninteractive_quit(EXIT_SUCCESS);
default:
- conn_info_usage();
+ cmd_usage(cmd);
+ optind = 0;
return noninteractive_quit(EXIT_FAILURE);
}
}
optind = 0;
if (argc < 1) {
- conn_info_usage();
+ cmd_usage(cmd);
return noninteractive_quit(EXIT_FAILURE);
}
noninteractive_quit(EXIT_SUCCESS);
}
-static void io_cap_usage(void)
-{
- print("Usage: io-cap <cap>");
-}
-
static void cmd_io_cap(struct mgmt *mgmt, uint16_t index,
int argc, char **argv)
{
uint8_t cap;
if (argc < 2) {
- io_cap_usage();
+ cmd_usage(argv[0]);
return noninteractive_quit(EXIT_FAILURE);
}
noninteractive_quit(EXIT_SUCCESS);
}
-static void scan_params_usage(void)
-{
- print("Usage: scan-params <interval> <window>");
-}
-
static void cmd_scan_params(struct mgmt *mgmt, uint16_t index,
int argc, char **argv)
{
struct mgmt_cp_set_scan_params cp;
if (argc < 3) {
- scan_params_usage();
+ cmd_usage(argv[0]);
return noninteractive_quit(EXIT_FAILURE);
}
noninteractive_quit(EXIT_SUCCESS);
}
-static void add_device_usage(void)
-{
- print("Usage: add-device [-a action] [-t type] <address>");
-}
-
static struct option add_device_options[] = {
{ "help", 0, 0, 'h' },
{ "action", 1, 0, 'a' },
uint8_t type = BDADDR_BREDR;
char addr[18];
int opt;
+ char *cmd = argv[0];
while ((opt = getopt_long(argc, argv, "+a:t:h", add_device_options,
NULL)) != -1) {
type = strtol(optarg, NULL, 0);
break;
case 'h':
- add_device_usage();
+ cmd_usage(cmd);
+ optind = 0;
return noninteractive_quit(EXIT_SUCCESS);
default:
- add_device_usage();
+ cmd_usage(cmd);
+ optind = 0;
return noninteractive_quit(EXIT_FAILURE);
}
}
optind = 0;
if (argc < 1) {
- add_device_usage();
+ cmd_usage(cmd);
return noninteractive_quit(EXIT_FAILURE);
}
noninteractive_quit(EXIT_SUCCESS);
}
-static void del_device_usage(void)
-{
- print("Usage: del-device [-t type] <address>");
-}
-
static struct option del_device_options[] = {
{ "help", 0, 0, 'h' },
{ "type", 1, 0, 't' },
uint8_t type = BDADDR_BREDR;
char addr[18];
int opt;
+ char *cmd = argv[0];
while ((opt = getopt_long(argc, argv, "+t:h", del_device_options,
NULL)) != -1) {
type = strtol(optarg, NULL, 0);
break;
case 'h':
- del_device_usage();
+ cmd_usage(cmd);
+ optind = 0;
return noninteractive_quit(EXIT_SUCCESS);
default:
- del_device_usage();
+ cmd_usage(cmd);
+ optind = 0;
return noninteractive_quit(EXIT_FAILURE);
}
}
optind = 0;
if (argc < 1) {
- del_device_usage();
+ cmd_usage(cmd);
return noninteractive_quit(EXIT_FAILURE);
}
static void advsize_usage(void)
{
- print("Usage: advsize [options] <instance_id>\nOptions:\n"
+ cmd_usage("advsize");
+ print("Options:\n"
"\t -c, --connectable \"connectable\" flag\n"
"\t -g, --general-discov \"general-discoverable\" flag\n"
"\t -l, --limited-discov \"limited-discoverable\" flag\n"
break;
default:
advsize_usage();
+ optind = 0;
return noninteractive_quit(EXIT_FAILURE);
}
}
static void add_adv_usage(void)
{
- print("Usage: add-adv [options] <instance_id>\nOptions:\n"
+ cmd_usage("add-adv");
+ print("Options:\n"
"\t -u, --uuid <uuid> Service UUID\n"
"\t -d, --adv-data <data> Advertising Data bytes\n"
"\t -s, --scan-rsp <data> Scan Response Data bytes\n"
break;
case 'h':
success = true;
+ /* fall through */
default:
add_adv_usage();
+ optind = 0;
goto done;
}
}
return noninteractive_quit(EXIT_SUCCESS);
}
-static void rm_adv_usage(void)
-{
- print("Usage: rm-adv <instance_id>");
-}
-
static void cmd_rm_adv(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
{
struct mgmt_cp_remove_advertising cp;
uint8_t instance;
if (argc != 2) {
- rm_adv_usage();
+ cmd_usage(argv[0]);
return noninteractive_quit(EXIT_FAILURE);
}
struct mgmt_cp_set_appearance cp;
if (argc < 2) {
- print("Usage: appearance <appearance>");
+ cmd_usage(argv[0]);
return noninteractive_quit(EXIT_FAILURE);
}
struct cmd_info {
char *cmd;
+ const char *arg;
void (*func)(struct mgmt *mgmt, uint16_t index, int argc, char **argv);
char *doc;
char * (*gen) (const char *text, int state);
};
static struct cmd_info all_cmd[] = {
- { "version", cmd_version, "Get the MGMT Version" },
- { "commands", cmd_commands, "List supported commands" },
- { "config", cmd_config, "Show configuration info" },
- { "info", cmd_info, "Show controller info" },
- { "extinfo", cmd_extinfo, "Show extended controller info" },
- { "auto-power", cmd_auto_power, "Power all available features" },
- { "power", cmd_power, "Toggle powered state" },
- { "discov", cmd_discov, "Toggle discoverable state" },
- { "connectable",cmd_connectable,"Toggle connectable state" },
- { "fast-conn", cmd_fast_conn, "Toggle fast connectable state" },
- { "bondable", cmd_bondable, "Toggle bondable state" },
- { "pairable", cmd_bondable, "Toggle bondable state" },
- { "linksec", cmd_linksec, "Toggle link level security" },
- { "ssp", cmd_ssp, "Toggle SSP mode" },
- { "sc", cmd_sc, "Toogle SC support" },
- { "hs", cmd_hs, "Toggle HS support" },
- { "le", cmd_le, "Toggle LE support" },
- { "advertising",cmd_advertising,"Toggle LE advertising", },
- { "bredr", cmd_bredr, "Toggle BR/EDR support", },
- { "privacy", cmd_privacy, "Toggle privacy support" },
- { "class", cmd_class, "Set device major/minor class" },
- { "disconnect", cmd_disconnect, "Disconnect device" },
- { "con", cmd_con, "List connections" },
- { "find", cmd_find, "Discover nearby devices" },
- { "find-service", cmd_find_service, "Discover nearby service" },
- { "stop-find", cmd_stop_find, "Stop discovery" },
- { "name", cmd_name, "Set local name" },
- { "pair", cmd_pair, "Pair with a remote device" },
- { "cancelpair", cmd_cancel_pair,"Cancel pairing" },
- { "unpair", cmd_unpair, "Unpair device" },
- { "keys", cmd_keys, "Load Link Keys" },
- { "ltks", cmd_ltks, "Load Long Term Keys" },
- { "irks", cmd_irks, "Load Identity Resolving Keys" },
- { "block", cmd_block, "Block Device" },
- { "unblock", cmd_unblock, "Unblock Device" },
- { "add-uuid", cmd_add_uuid, "Add UUID" },
- { "rm-uuid", cmd_remove_uuid,"Remove UUID" },
- { "clr-uuids", cmd_clr_uuids, "Clear UUIDs" },
- { "local-oob", cmd_local_oob, "Local OOB data" },
- { "remote-oob", cmd_remote_oob, "Remote OOB data" },
- { "did", cmd_did, "Set Device ID" },
- { "static-addr",cmd_static_addr,"Set static address" },
- { "public-addr",cmd_public_addr,"Set public address" },
- { "ext-config", cmd_ext_config, "External configuration" },
- { "debug-keys", cmd_debug_keys, "Toogle debug keys" },
- { "conn-info", cmd_conn_info, "Get connection information" },
- { "io-cap", cmd_io_cap, "Set IO Capability" },
- { "scan-params",cmd_scan_params,"Set Scan Parameters" },
- { "get-clock", cmd_clock_info, "Get Clock Information" },
- { "add-device", cmd_add_device, "Add Device" },
- { "del-device", cmd_del_device, "Remove Device" },
- { "clr-devices",cmd_clr_devices,"Clear Devices" },
- { "bredr-oob", cmd_bredr_oob, "Local OOB data (BR/EDR)" },
- { "le-oob", cmd_le_oob, "Local OOB data (LE)" },
- { "advinfo", cmd_advinfo, "Show advertising features" },
- { "advsize", cmd_advsize, "Show advertising size info" },
- { "add-adv", cmd_add_adv, "Add advertising instance" },
- { "rm-adv", cmd_rm_adv, "Remove advertising instance" },
- { "clr-adv", cmd_clr_adv, "Clear advertising instances" },
- { "appearance", cmd_appearance, "Set appearance" },
+ { "version", NULL,
+ cmd_version, "Get the MGMT Version" },
+ { "commands", NULL,
+ cmd_commands, "List supported commands" },
+ { "config", NULL,
+ cmd_config, "Show configuration info" },
+ { "info", NULL,
+ cmd_info, "Show controller info" },
+ { "extinfo", NULL,
+ cmd_extinfo, "Show extended controller info" },
+ { "auto-power", NULL,
+ cmd_auto_power, "Power all available features" },
+ { "power", "<on/off>",
+ cmd_power, "Toggle powered state" },
+ { "discov", "<yes/no/limited> [timeout]",
+ cmd_discov, "Toggle discoverable state" },
+ { "connectable", "<on/off>",
+ cmd_connectable, "Toggle connectable state" },
+ { "fast-conn", "<on/off>",
+ cmd_fast_conn, "Toggle fast connectable state" },
+ { "bondable", "<on/off>",
+ cmd_bondable, "Toggle bondable state" },
+ { "pairable", "<on/off>",
+ cmd_bondable, "Toggle bondable state" },
+ { "linksec", "<on/off>",
+ cmd_linksec, "Toggle link level security" },
+ { "ssp", "<on/off>",
+ cmd_ssp, "Toggle SSP mode" },
+ { "sc", "<on/off/only>",
+ cmd_sc, "Toogle SC support" },
+ { "hs", "<on/off>",
+ cmd_hs, "Toggle HS support" },
+ { "le", "<on/off>",
+ cmd_le, "Toggle LE support" },
+ { "advertising", "<on/off>",
+ cmd_advertising, "Toggle LE advertising", },
+ { "bredr", "<on/off>",
+ cmd_bredr, "Toggle BR/EDR support", },
+ { "privacy", "<on/off>",
+ cmd_privacy, "Toggle privacy support" },
+ { "class", "<major> <minor>",
+ cmd_class, "Set device major/minor class" },
+ { "disconnect", "[-t type] <remote address>",
+ cmd_disconnect, "Disconnect device" },
+ { "con", NULL,
+ cmd_con, "List connections" },
+ { "find", "[-l|-b] [-L]",
+ cmd_find, "Discover nearby devices" },
+ { "find-service", "[-u UUID] [-r RSSI_Threshold] [-l|-b]",
+ cmd_find_service, "Discover nearby service" },
+ { "stop-find", "[-l|-b]",
+ cmd_stop_find, "Stop discovery" },
+ { "name", "<name> [shortname]",
+ cmd_name, "Set local name" },
+ { "pair", "[-c cap] [-t type] <remote address>",
+ cmd_pair, "Pair with a remote device" },
+ { "cancelpair", "[-t type] <remote address>",
+ cmd_cancel_pair, "Cancel pairing" },
+ { "unpair", "[-t type] <remote address>",
+ cmd_unpair, "Unpair device" },
+ { "keys", NULL,
+ cmd_keys, "Load Link Keys" },
+ { "ltks", NULL,
+ cmd_ltks, "Load Long Term Keys" },
+ { "irks", "[--local <index>] [--file <file path>]",
+ cmd_irks, "Load Identity Resolving Keys" },
+ { "block", "[-t type] <remote address>",
+ cmd_block, "Block Device" },
+ { "unblock", "[-t type] <remote address>",
+ cmd_unblock, "Unblock Device" },
+ { "add-uuid", "<UUID> <service class hint>",
+ cmd_add_uuid, "Add UUID" },
+ { "rm-uuid", "<UUID>",
+ cmd_remove_uuid, "Remove UUID" },
+ { "clr-uuids", NULL,
+ cmd_clr_uuids, "Clear UUIDs" },
+ { "local-oob", NULL,
+ cmd_local_oob, "Local OOB data" },
+ { "remote-oob", "[-t <addr_type>] [-r <rand192>] "
+ "[-h <hash192>] [-R <rand256>] "
+ "[-H <hash256>] <addr>",
+ cmd_remote_oob, "Remote OOB data" },
+ { "did", "<source>:<vendor>:<product>:<version>",
+ cmd_did, "Set Device ID" },
+ { "static-addr", "<address>",
+ cmd_static_addr, "Set static address" },
+ { "public-addr", "<address>",
+ cmd_public_addr, "Set public address" },
+ { "ext-config", "<on/off>",
+ cmd_ext_config, "External configuration" },
+ { "debug-keys", "<on/off>",
+ cmd_debug_keys, "Toogle debug keys" },
+ { "conn-info", "[-t type] <remote address>",
+ cmd_conn_info, "Get connection information" },
+ { "io-cap", "<cap>",
+ cmd_io_cap, "Set IO Capability" },
+ { "scan-params", "<interval> <window>",
+ cmd_scan_params, "Set Scan Parameters" },
+ { "get-clock", "[address]",
+ cmd_clock_info, "Get Clock Information" },
+ { "add-device", "[-a action] [-t type] <address>",
+ cmd_add_device, "Add Device" },
+ { "del-device", "[-t type] <address>",
+ cmd_del_device, "Remove Device" },
+ { "clr-devices", NULL,
+ cmd_clr_devices, "Clear Devices" },
+ { "bredr-oob", NULL,
+ cmd_bredr_oob, "Local OOB data (BR/EDR)" },
+ { "le-oob", NULL,
+ cmd_le_oob, "Local OOB data (LE)" },
+ { "advinfo", NULL,
+ cmd_advinfo, "Show advertising features" },
+ { "advsize", "[options] <instance_id>",
+ cmd_advsize, "Show advertising size info" },
+ { "add-adv", "[options] <instance_id>",
+ cmd_add_adv, "Add advertising instance" },
+ { "rm-adv", "<instance_id>",
+ cmd_rm_adv, "Remove advertising instance" },
+ { "clr-adv", NULL,
+ cmd_clr_adv, "Clear advertising instances" },
+ { "appearance", "<appearance>",
+ cmd_appearance, "Set appearance" },
};
static void cmd_quit(struct mgmt *mgmt, uint16_t index,
int argc, char **argv)
{
if (argc != 2) {
- error("Usage: select <index>");
+ cmd_usage(argv[0]);
return;
}
mgmt_cancel_all(mgmt);
mgmt_unregister_all(mgmt);
- if (!strcmp(argv[1], "none") || !strcmp(argv[1], "any") ||
- !strcmp(argv[1], "all"))
- mgmt_index = MGMT_INDEX_NONE;
- else if (!strncmp(argv[1], "hci", 3))
- mgmt_index = atoi(&argv[1][3]);
- else
- mgmt_index = atoi(argv[1]);
+ set_index(argv[1]);
register_mgmt_callbacks(mgmt, mgmt_index);
}
static struct cmd_info interactive_cmd[] = {
- { "select", cmd_select, "Select a different index" },
- { "quit", cmd_quit, "Exit program" },
- { "exit", cmd_quit, "Exit program" },
- { "help", NULL, "List supported commands" },
+ { "select", "<index>",
+ cmd_select, "Select a different index" },
+ { "quit", NULL,
+ cmd_quit, "Exit program" },
+ { "exit", NULL,
+ cmd_quit, "Exit program" },
+ { "help", NULL,
+ NULL, "List supported commands" },
};
static char *cmd_generator(const char *text, int state)
return NULL;
}
+static void cmd_usage(char *cmd)
+{
+ struct cmd_info *c;
+
+ if (!cmd)
+ return;
+
+ c = find_cmd(cmd, all_cmd, NELEM(all_cmd));
+ if (!c && interactive) {
+ c = find_cmd(cmd, interactive_cmd, NELEM(interactive_cmd));
+ if (!c)
+ return;
+ error("Usage: %s %s", cmd, c->arg ? : "");
+ return;
+ }
+
+ if (!c)
+ return;
+
+ print("Usage: %s %s", cmd, c->arg ? : "");
+
+}
+
static void rl_handler(char *input)
{
struct cmd_info *c;
if (prompt_input(input))
goto done;
- add_history(input);
+ if (history_search(input, -1))
+ add_history(input);
if (wordexp(input, &w, WRDE_NOCMD))
goto done;
for (i = 0; i < NELEM(all_cmd); i++) {
c = &all_cmd[i];
- if (c->doc)
- print(" %s %-*s %s", c->cmd,
- (int)(25 - strlen(c->cmd)), "", c->doc ? : "");
+ if ((int)strlen(c->arg ? : "") <=
+ (int)(25 - strlen(c->cmd)))
+ printf(" %s %-*s %s\n", c->cmd,
+ (int)(25 - strlen(c->cmd)),
+ c->arg ? : "",
+ c->doc ? : "");
+ else
+ printf(" %s %-s\n" " %s %-25s %s\n",
+ c->cmd,
+ c->arg ? : "",
+ "", "",
+ c->doc ? : "");
}
if (!interactive)
for (i = 0; i < NELEM(interactive_cmd); i++) {
c = &interactive_cmd[i];
- if (c->doc)
- print(" %s %-*s %s", c->cmd,
- (int)(25 - strlen(c->cmd)), "", c->doc ? : "");
+ if ((int)strlen(c->arg ? : "") <=
+ (int)(25 - strlen(c->cmd)))
+ printf(" %s %-*s %s\n", c->cmd,
+ (int)(25 - strlen(c->cmd)),
+ c->arg ? : "",
+ c->doc ? : "");
+ else
+ printf(" %s %-s\n" " %s %-25s %s\n",
+ c->cmd,
+ c->arg ? : "",
+ "", "",
+ c->doc ? : "");
}
free_we:
int main(int argc, char *argv[])
{
struct io *input;
- uint16_t index = MGMT_INDEX_NONE;
int status, opt;
while ((opt = getopt_long(argc, argv, "+hi:",
main_options, NULL)) != -1) {
switch (opt) {
case 'i':
- if (strlen(optarg) > 3 &&
- strncasecmp(optarg, "hci", 3) == 0)
- index = atoi(optarg + 3);
- else
- index = atoi(optarg);
+ set_index(optarg);
break;
case 'h':
default:
return EXIT_FAILURE;
}
- c->func(mgmt, index, argc, argv);
+ c->func(mgmt, mgmt_index, argc, argv);
}
- register_mgmt_callbacks(mgmt, index);
+ register_mgmt_callbacks(mgmt, mgmt_index);
/* Interactive mode */
if (!argc)
rl_erase_empty_line = 1;
rl_callback_handler_install(NULL, rl_handler);
- update_prompt(index);
+ update_prompt(mgmt_index);
rl_redisplay();
}
- mgmt_index = index;
-
status = mainloop_run();
if (input) {
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011-2017 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>
+#include <unistd.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <getopt.h>
+
+#include <ell/ell.h>
+
+#include "src/shared/btp.h"
+
+struct btp_adapter {
+ struct l_dbus_proxy *proxy;
+ uint8_t index;
+ uint32_t supported_settings;
+ uint32_t current_settings;
+ struct l_queue *devices;
+};
+
+struct btp_device {
+ struct l_dbus_proxy *proxy;
+};
+
+static struct l_queue *adapters;
+static char *socket_path;
+static struct btp *btp;
+
+static bool gap_service_registered;
+
+static struct btp_adapter *find_adapter_by_proxy(struct l_dbus_proxy *proxy)
+{
+ const struct l_queue_entry *entry;
+
+ for (entry = l_queue_get_entries(adapters); entry;
+ entry = entry->next) {
+ struct btp_adapter *adapter = entry->data;
+
+ if (adapter->proxy == proxy)
+ return adapter;
+ }
+
+ return NULL;
+}
+
+static struct btp_adapter *find_adapter_by_index(uint8_t index)
+{
+ const struct l_queue_entry *entry;
+
+ for (entry = l_queue_get_entries(adapters); entry;
+ entry = entry->next) {
+ struct btp_adapter *adapter = entry->data;
+
+ if (adapter->index == index)
+ return adapter;
+ }
+
+ return NULL;
+}
+
+static struct btp_adapter *find_adapter_by_path(const char *path)
+{
+ const struct l_queue_entry *entry;
+
+ for (entry = l_queue_get_entries(adapters); entry;
+ entry = entry->next) {
+ struct btp_adapter *adapter = entry->data;
+
+ if (!strcmp(l_dbus_proxy_get_path(adapter->proxy), path))
+ return adapter;
+ }
+
+ return NULL;
+}
+
+static void btp_gap_read_commands(uint8_t index, const void *param,
+ uint16_t length, void *user_data)
+{
+ uint16_t commands = 0;
+
+ if (index != BTP_INDEX_NON_CONTROLLER) {
+ btp_send_error(btp, BTP_GAP_SERVICE, index,
+ BTP_ERROR_INVALID_INDEX);
+ return;
+ }
+
+ commands |= (1 << BTP_OP_GAP_READ_SUPPORTED_COMMANDS);
+ commands |= (1 << BTP_OP_GAP_READ_CONTROLLER_INDEX_LIST);
+ commands |= (1 << BTP_OP_GAP_READ_COTROLLER_INFO);
+ commands |= (1 << BTP_OP_GAP_RESET);
+ commands |= (1 << BTP_OP_GAP_SET_POWERED);
+ commands |= (1 << BTP_OP_GAP_SET_DISCOVERABLE);
+ commands |= (1 << BTP_OP_GAP_SET_BONDABLE);
+
+ commands = L_CPU_TO_LE16(commands);
+
+ btp_send(btp, BTP_GAP_SERVICE, BTP_OP_GAP_READ_SUPPORTED_COMMANDS,
+ BTP_INDEX_NON_CONTROLLER, sizeof(commands), &commands);
+}
+
+static void btp_gap_read_controller_index(uint8_t index, const void *param,
+ uint16_t length, void *user_data)
+{
+ const struct l_queue_entry *entry;
+ struct btp_gap_read_index_rp *rp;
+ uint8_t cnt;
+ int i;
+
+ if (index != BTP_INDEX_NON_CONTROLLER) {
+ btp_send_error(btp, BTP_GAP_SERVICE, index,
+ BTP_ERROR_INVALID_INDEX);
+ return;
+ }
+
+ cnt = l_queue_length(adapters);
+
+ rp = l_malloc(sizeof(*rp) + cnt);
+
+ rp->num = cnt;
+
+ for (i = 0, entry = l_queue_get_entries(adapters); entry;
+ i++, entry = entry->next) {
+ struct btp_adapter *adapter = entry->data;
+
+ rp->indexes[i] = adapter->index;
+ }
+
+ btp_send(btp, BTP_GAP_SERVICE, BTP_OP_GAP_READ_CONTROLLER_INDEX_LIST,
+ BTP_INDEX_NON_CONTROLLER, sizeof(*rp) + cnt, rp);
+}
+
+static void btp_gap_read_info(uint8_t index, const void *param, uint16_t length,
+ void *user_data)
+{
+ struct btp_adapter *adapter = find_adapter_by_index(index);
+ struct btp_gap_read_info_rp rp;
+ const char *str;
+ uint8_t status = BTP_ERROR_FAIL;
+
+ if (!adapter) {
+ status = BTP_ERROR_INVALID_INDEX;
+ goto failed;
+ }
+
+ memset(&rp, 0, sizeof(rp));
+
+ if (!l_dbus_proxy_get_property(adapter->proxy, "Address", "s", &str))
+ goto failed;
+
+ if (sscanf(str,"%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
+ &rp.address[5], &rp.address[4], &rp.address[3],
+ &rp.address[2], &rp.address[1], &rp.address[0]) != 6)
+ goto failed;
+
+ if (!l_dbus_proxy_get_property(adapter->proxy, "Name", "s", &str)) {
+ goto failed;
+ }
+
+ strncpy((char *) rp.name, str, sizeof(rp.name));
+ strncpy((char *) rp.short_name, str, sizeof(rp.short_name));
+ rp.supported_settings = L_CPU_TO_LE32(adapter->supported_settings);
+ rp.current_settings = L_CPU_TO_LE32(adapter->current_settings);
+
+ btp_send(btp, BTP_GAP_SERVICE, BTP_OP_GAP_READ_COTROLLER_INFO, index,
+ sizeof(rp), &rp);
+
+ return;
+failed:
+ btp_send_error(btp, BTP_GAP_SERVICE, index, status);
+}
+
+static void remove_device_setup(struct l_dbus_message *message,
+ void *user_data)
+{
+ struct btp_device *device = user_data;
+
+ l_dbus_message_set_arguments(message, "o",
+ l_dbus_proxy_get_path(device->proxy));
+}
+
+static void remove_device_reply(struct l_dbus_proxy *proxy,
+ struct l_dbus_message *result,
+ void *user_data)
+{
+ struct btp_device *device = user_data;
+ struct btp_adapter *adapter = find_adapter_by_proxy(proxy);
+
+ if (!adapter)
+ return;
+
+ if (l_dbus_message_is_error(result)) {
+ const char *name;
+
+ l_dbus_message_get_error(result, &name, NULL);
+
+ l_error("Failed to remove device %s (%s)",
+ l_dbus_proxy_get_path(device->proxy),
+ name);
+ return;
+ }
+
+ l_queue_remove(adapter->devices, device);
+}
+
+static void btp_gap_reset(uint8_t index, const void *param, uint16_t length,
+ void *user_data)
+{
+ struct btp_adapter *adapter = find_adapter_by_index(index);
+ const struct l_queue_entry *entry;
+ uint8_t status;
+ bool prop;
+
+ if (!adapter) {
+ status = BTP_ERROR_INVALID_INDEX;
+ goto failed;
+ }
+
+ /* Adapter needs to be powered to be able to remove devices */
+ if (!l_dbus_proxy_get_property(adapter->proxy, "Powered", "b", &prop) ||
+ !prop) {
+ status = BTP_ERROR_FAIL;
+ goto failed;
+ }
+
+ for (entry = l_queue_get_entries(adapter->devices); entry;
+ entry = entry->next) {
+ struct btp_device *device = entry->data;
+
+ l_dbus_proxy_method_call(adapter->proxy, "RemoveDevice",
+ remove_device_setup,
+ remove_device_reply, device,
+ NULL);
+ }
+
+ /* TODO for we assume all went well */
+ btp_send(btp, BTP_GAP_SERVICE, BTP_OP_GAP_RESET, index, 0, NULL);
+ return;
+
+failed:
+ btp_send_error(btp, BTP_GAP_SERVICE, index, status);
+}
+
+struct set_setting_data {
+ struct btp_adapter *adapter;
+ uint8_t opcode;
+ uint32_t setting;
+ bool value;
+};
+
+static void set_setting_reply(struct l_dbus_proxy *proxy,
+ struct l_dbus_message *result, void *user_data)
+{
+ struct set_setting_data *data = user_data;
+ struct btp_adapter *adapter = data->adapter;
+ uint32_t settings;
+
+ if (l_dbus_message_is_error(result)) {
+ btp_send_error(btp, BTP_GAP_SERVICE, data->adapter->index,
+ BTP_ERROR_FAIL);
+ return;
+ }
+
+ if (data->value)
+ adapter->current_settings |= data->setting;
+ else
+ adapter->current_settings &= ~data->setting;
+
+ settings = L_CPU_TO_LE32(adapter->current_settings);
+
+ btp_send(btp, BTP_GAP_SERVICE, data->opcode, adapter->index,
+ sizeof(settings), &settings);
+}
+
+static void btp_gap_set_powered(uint8_t index, const void *param,
+ uint16_t length, void *user_data)
+{
+ struct btp_adapter *adapter = find_adapter_by_index(index);
+ const struct btp_gap_set_powered_cp *cp = param;
+ uint8_t status = BTP_ERROR_FAIL;
+ struct set_setting_data *data;
+
+ if (length < sizeof(*cp))
+ goto failed;
+
+ if (!adapter) {
+ status = BTP_ERROR_INVALID_INDEX;
+ goto failed;
+ }
+
+ data = l_new(struct set_setting_data, 1);
+ data->adapter = adapter;
+ data->opcode = BTP_OP_GAP_SET_POWERED;
+ data->setting = BTP_GAP_SETTING_POWERED;
+ data->value = cp->powered;
+
+ if (l_dbus_proxy_set_property(adapter->proxy, set_setting_reply,
+ data, l_free, "Powered", "b",
+ data->value))
+ return;
+
+ l_free(data);
+
+failed:
+ btp_send_error(btp, BTP_GAP_SERVICE, index, status);
+}
+
+static void btp_gap_set_discoverable(uint8_t index, const void *param,
+ uint16_t length, void *user_data)
+{
+ struct btp_adapter *adapter = find_adapter_by_index(index);
+ const struct btp_gap_set_discoverable_cp *cp = param;
+ uint8_t status = BTP_ERROR_FAIL;
+ struct set_setting_data *data;
+
+ if (length < sizeof(*cp))
+ goto failed;
+
+ if (!adapter) {
+ status = BTP_ERROR_INVALID_INDEX;
+ goto failed;
+ }
+
+ data = l_new(struct set_setting_data, 1);
+ data->adapter = adapter;
+ data->opcode = BTP_OP_GAP_SET_DISCOVERABLE;
+ data->setting = BTP_GAP_SETTING_DISCOVERABLE;
+ data->value = cp->discoverable;
+
+ if (l_dbus_proxy_set_property(adapter->proxy, set_setting_reply,
+ data, l_free, "Discoverable", "b",
+ data->value))
+ return;
+
+ l_free(data);
+
+failed:
+ btp_send_error(btp, BTP_GAP_SERVICE, index, status);
+}
+
+static void btp_gap_set_bondable(uint8_t index, const void *param,
+ uint16_t length, void *user_data)
+{
+ struct btp_adapter *adapter = find_adapter_by_index(index);
+ const struct btp_gap_set_bondable_cp *cp = param;
+ uint8_t status = BTP_ERROR_FAIL;
+ struct set_setting_data *data;
+
+ if (length < sizeof(*cp))
+ goto failed;
+
+ if (!adapter) {
+ status = BTP_ERROR_INVALID_INDEX;
+ goto failed;
+ }
+
+ data = l_new(struct set_setting_data, 1);
+ data->adapter = adapter;
+ data->opcode = BTP_OP_GAP_SET_BONDABLE;
+ data->setting = BTP_GAP_SETTING_BONDABLE;
+ data->value = cp->bondable;
+
+ if (l_dbus_proxy_set_property(adapter->proxy, set_setting_reply,
+ data, l_free, "Pairable", "b",
+ data->value))
+ return;
+
+ l_free(data);
+
+failed:
+ btp_send_error(btp, BTP_GAP_SERVICE, index, status);
+}
+
+static void register_gap_service(void)
+{
+ btp_register(btp, BTP_GAP_SERVICE, BTP_OP_GAP_READ_SUPPORTED_COMMANDS,
+ btp_gap_read_commands, NULL, NULL);
+
+ btp_register(btp, BTP_GAP_SERVICE,
+ BTP_OP_GAP_READ_CONTROLLER_INDEX_LIST,
+ btp_gap_read_controller_index, NULL, NULL);
+
+ btp_register(btp, BTP_GAP_SERVICE, BTP_OP_GAP_READ_COTROLLER_INFO,
+ btp_gap_read_info, NULL, NULL);
+
+ btp_register(btp, BTP_GAP_SERVICE, BTP_OP_GAP_RESET,
+ btp_gap_reset, NULL, NULL);
+
+ btp_register(btp, BTP_GAP_SERVICE, BTP_OP_GAP_SET_POWERED,
+ btp_gap_set_powered, NULL, NULL);
+
+ btp_register(btp, BTP_GAP_SERVICE, BTP_OP_GAP_SET_DISCOVERABLE,
+ btp_gap_set_discoverable, NULL, NULL);
+
+ btp_register(btp, BTP_GAP_SERVICE, BTP_OP_GAP_SET_BONDABLE,
+ btp_gap_set_bondable, NULL, NULL);
+}
+
+static void btp_core_read_commands(uint8_t index, const void *param,
+ uint16_t length, void *user_data)
+{
+ uint8_t commands = 0;
+
+ if (index != BTP_INDEX_NON_CONTROLLER) {
+ btp_send_error(btp, BTP_CORE_SERVICE, index,
+ BTP_ERROR_INVALID_INDEX);
+ return;
+ }
+
+ commands |= (1 << BTP_OP_CORE_READ_SUPPORTED_COMMANDS);
+ commands |= (1 << BTP_OP_CORE_READ_SUPPORTED_SERVICES);
+ commands |= (1 << BTP_OP_CORE_REGISTER);
+ commands |= (1 << BTP_OP_CORE_UNREGISTER);
+
+ btp_send(btp, BTP_CORE_SERVICE, BTP_OP_CORE_READ_SUPPORTED_COMMANDS,
+ BTP_INDEX_NON_CONTROLLER, sizeof(commands), &commands);
+}
+
+static void btp_core_read_services(uint8_t index, const void *param,
+ uint16_t length, void *user_data)
+{
+ uint8_t services = 0;
+
+ if (index != BTP_INDEX_NON_CONTROLLER) {
+ btp_send_error(btp, BTP_CORE_SERVICE, index,
+ BTP_ERROR_INVALID_INDEX);
+ return;
+ }
+
+ services |= (1 << BTP_CORE_SERVICE);
+ services |= (1 << BTP_GAP_SERVICE);
+
+ btp_send(btp, BTP_CORE_SERVICE, BTP_OP_CORE_READ_SUPPORTED_SERVICES,
+ BTP_INDEX_NON_CONTROLLER, sizeof(services), &services);
+}
+
+static void btp_core_register(uint8_t index, const void *param,
+ uint16_t length, void *user_data)
+{
+ const struct btp_core_register_cp *cp = param;
+
+ if (length < sizeof(*cp))
+ goto failed;
+
+ if (index != BTP_INDEX_NON_CONTROLLER) {
+ btp_send_error(btp, BTP_CORE_SERVICE, index,
+ BTP_ERROR_INVALID_INDEX);
+ return;
+ }
+
+ switch (cp->service_id) {
+ case BTP_GAP_SERVICE:
+ if (gap_service_registered)
+ goto failed;
+
+ register_gap_service();
+ gap_service_registered = true;
+ break;
+ case BTP_GATT_SERVICE:
+ case BTP_L2CAP_SERVICE:
+ case BTP_MESH_NODE_SERVICE:
+ case BTP_CORE_SERVICE:
+ default:
+ goto failed;
+ }
+
+ btp_send(btp, BTP_CORE_SERVICE, BTP_OP_CORE_REGISTER,
+ BTP_INDEX_NON_CONTROLLER, 0, NULL);
+ return;
+
+failed:
+ btp_send_error(btp, BTP_CORE_SERVICE, index, BTP_ERROR_FAIL);
+}
+
+static void btp_core_unregister(uint8_t index, const void *param,
+ uint16_t length, void *user_data)
+{
+ const struct btp_core_unregister_cp *cp = param;
+
+ if (length < sizeof(*cp))
+ goto failed;
+
+ if (index != BTP_INDEX_NON_CONTROLLER) {
+ btp_send_error(btp, BTP_CORE_SERVICE, index,
+ BTP_ERROR_INVALID_INDEX);
+ return;
+ }
+
+ switch (cp->service_id) {
+ case BTP_GAP_SERVICE:
+ if (!gap_service_registered)
+ goto failed;
+
+ btp_unregister_service(btp, BTP_GAP_SERVICE);
+ gap_service_registered = false;
+ break;
+ case BTP_GATT_SERVICE:
+ case BTP_L2CAP_SERVICE:
+ case BTP_MESH_NODE_SERVICE:
+ case BTP_CORE_SERVICE:
+ default:
+ goto failed;
+ }
+
+ btp_send(btp, BTP_CORE_SERVICE, BTP_OP_CORE_UNREGISTER,
+ BTP_INDEX_NON_CONTROLLER, 0, NULL);
+ return;
+
+failed:
+ btp_send_error(btp, BTP_CORE_SERVICE, index, BTP_ERROR_FAIL);
+}
+
+static void register_core_service(void)
+{
+ btp_register(btp, BTP_CORE_SERVICE,
+ BTP_OP_CORE_READ_SUPPORTED_COMMANDS,
+ btp_core_read_commands, NULL, NULL);
+
+ btp_register(btp, BTP_CORE_SERVICE,
+ BTP_OP_CORE_READ_SUPPORTED_SERVICES,
+ btp_core_read_services, NULL, NULL);
+
+ btp_register(btp, BTP_CORE_SERVICE, BTP_OP_CORE_REGISTER,
+ btp_core_register, NULL, NULL);
+
+ btp_register(btp, BTP_CORE_SERVICE, BTP_OP_CORE_UNREGISTER,
+ btp_core_unregister, NULL, NULL);
+}
+
+static void signal_handler(struct l_signal *signal, uint32_t signo,
+ void *user_data)
+{
+ switch (signo) {
+ case SIGINT:
+ case SIGTERM:
+ l_info("Terminating");
+ l_main_quit();
+ break;
+ }
+}
+
+static void btp_device_free(struct btp_device *device)
+{
+ l_free(device);
+}
+
+static void btp_adapter_free(struct btp_adapter *adapter)
+{
+ l_queue_destroy(adapter->devices,
+ (l_queue_destroy_func_t)btp_device_free);
+ l_free(adapter);
+}
+
+static void extract_settings(struct l_dbus_proxy *proxy, uint32_t *current,
+ uint32_t *supported)
+{
+ bool prop;
+
+ *supported = 0;
+ *current = 0;
+
+ /* TODO not all info is available via D-Bus API */
+ *supported |= BTP_GAP_SETTING_POWERED;
+ *supported |= BTP_GAP_SETTING_CONNECTABLE;
+ *supported |= BTP_GAP_SETTING_DISCOVERABLE;
+ *supported |= BTP_GAP_SETTING_BONDABLE;
+ *supported |= BTP_GAP_SETTING_SSP;
+ *supported |= BTP_GAP_SETTING_BREDR;
+ *supported |= BTP_GAP_SETTING_LE;
+ *supported |= BTP_GAP_SETTING_ADVERTISING;
+ *supported |= BTP_GAP_SETTING_SC;
+ *supported |= BTP_GAP_SETTING_PRIVACY;
+ /* *supported |= BTP_GAP_SETTING_STATIC_ADDRESS; */
+
+ /* TODO not all info is availbe via D-Bus API so some are assumed to be
+ * enabled by bluetoothd or simply hardcoded until API is extended
+ */
+ *current |= BTP_GAP_SETTING_CONNECTABLE;
+ *current |= BTP_GAP_SETTING_SSP;
+ *current |= BTP_GAP_SETTING_BREDR;
+ *current |= BTP_GAP_SETTING_LE;
+ *current |= BTP_GAP_SETTING_PRIVACY;
+ *current |= BTP_GAP_SETTING_SC;
+ /* *supported |= BTP_GAP_SETTING_STATIC_ADDRESS; */
+
+ if (l_dbus_proxy_get_property(proxy, "Powered", "b", &prop) && prop)
+ *current |= BTP_GAP_SETTING_POWERED;
+
+ if (l_dbus_proxy_get_property(proxy, "Discoverable", "b", &prop) &&
+ prop)
+ *current |= BTP_GAP_SETTING_DISCOVERABLE;
+
+ if (l_dbus_proxy_get_property(proxy, "Pairable", "b", &prop) && prop)
+ *current |= BTP_GAP_SETTING_BONDABLE;
+}
+
+static void proxy_added(struct l_dbus_proxy *proxy, void *user_data)
+{
+ const char *interface = l_dbus_proxy_get_interface(proxy);
+ const char *path = l_dbus_proxy_get_path(proxy);
+
+ l_info("Proxy added: %s (%s)", interface, path);
+
+ if (!strcmp(interface, "org.bluez.Adapter1")) {
+ struct btp_adapter *adapter;
+
+ adapter = l_new(struct btp_adapter, 1);
+ adapter->proxy = proxy;
+ adapter->index = l_queue_length(adapters);
+ adapter->devices = l_queue_new();
+
+ extract_settings(proxy, &adapter->current_settings,
+ &adapter->supported_settings);
+
+ l_queue_push_tail(adapters, adapter);
+ return;
+ }
+
+ if (!strcmp(interface, "org.bluez.Device1")) {
+ struct btp_adapter *adapter;
+ struct btp_device *device;
+ char *str;
+
+ if (!l_dbus_proxy_get_property(proxy, "Adapter", "o", &str))
+ return;
+
+ adapter = find_adapter_by_path(str);
+ if (!adapter)
+ return;
+
+ device = l_new(struct btp_device, 1);
+ device->proxy = proxy;
+
+ l_queue_push_tail(adapter->devices, device);
+ return;
+ }
+}
+
+static bool device_match_by_proxy(const void *a, const void *b)
+{
+ const struct btp_device *device = a;
+ const struct l_dbus_proxy *proxy = b;
+
+ return device->proxy == proxy;
+}
+
+static void proxy_removed(struct l_dbus_proxy *proxy, void *user_data)
+{
+ const char *interface = l_dbus_proxy_get_interface(proxy);
+ const char *path = l_dbus_proxy_get_path(proxy);
+
+ l_info("Proxy removed: %s (%s)", interface, path);
+
+ if (!strcmp(interface, "org.bluez.Adapter1")) {
+ l_info("Adapter removed, terminating.");
+ l_main_quit();
+ return;
+ }
+
+ if (!strcmp(interface, "org.bluez.Device1")) {
+ struct btp_adapter *adapter;
+ char *str;
+
+ if (!l_dbus_proxy_get_property(proxy, "Adapter", "o", &str))
+ return;
+
+ adapter = find_adapter_by_path(str);
+ if (!adapter)
+ return;
+
+ l_queue_remove_if(adapter->devices, device_match_by_proxy,
+ proxy);
+
+ return;
+ }
+}
+
+static void update_current_settings(struct btp_adapter *adapter,
+ uint32_t new_settings)
+{
+ struct btp_new_settings_ev ev;
+
+ adapter->current_settings = new_settings;
+
+ ev.current_settings = L_CPU_TO_LE32(adapter->current_settings);
+
+ btp_send(btp, BTP_GAP_SERVICE, BTP_EV_GAP_NEW_SETTINGS, adapter->index,
+ sizeof(ev), &ev);
+}
+
+static void property_changed(struct l_dbus_proxy *proxy, const char *name,
+ struct l_dbus_message *msg, void *user_data)
+{
+ const char *interface = l_dbus_proxy_get_interface(proxy);
+ const char *path = l_dbus_proxy_get_path(proxy);
+
+ l_info("property_changed %s %s %s", name, path, interface);
+
+ if (!strcmp(interface, "org.bluez.Adapter1")) {
+ struct btp_adapter *adapter = find_adapter_by_proxy(proxy);
+ uint32_t new_settings;
+
+ if (!adapter)
+ return;
+
+ new_settings = adapter->current_settings;
+
+ if (!strcmp(name, "Powered")) {
+ bool prop;
+
+ if (!l_dbus_message_get_arguments(msg, "b", &prop))
+ return;
+
+ if (prop)
+ new_settings |= BTP_GAP_SETTING_POWERED;
+ else
+ new_settings &= ~BTP_GAP_SETTING_POWERED;
+ } else if (!strcmp(name, "Discoverable")) {
+ bool prop;
+
+ if (!l_dbus_message_get_arguments(msg, "b", &prop))
+ return;
+
+ if (prop)
+ new_settings |= BTP_GAP_SETTING_DISCOVERABLE;
+ else
+ new_settings &= ~BTP_GAP_SETTING_DISCOVERABLE;
+ }
+
+ if (!strcmp(name, "Pairable")) {
+ bool prop;
+
+ if (!l_dbus_message_get_arguments(msg, "b", &prop))
+ return;
+
+ if (prop)
+ new_settings |= BTP_GAP_SETTING_BONDABLE;
+ else
+ new_settings &= ~BTP_GAP_SETTING_BONDABLE;
+ }
+
+ if (new_settings != adapter->current_settings)
+ update_current_settings(adapter, new_settings);
+
+ return;
+ }
+}
+
+static void client_connected(struct l_dbus *dbus, void *user_data)
+{
+ l_info("D-Bus client connected");
+}
+
+static void client_disconnected(struct l_dbus *dbus, void *user_data)
+{
+ l_info("D-Bus client disconnected, terminated");
+ l_main_quit();
+}
+
+static void client_ready(struct l_dbus_client *client, void *user_data)
+{
+ l_info("D-Bus client ready, connecting BTP");
+
+ btp = btp_new(socket_path);
+ if (!btp) {
+ l_error("Failed to connect BTP, terminating");
+ l_main_quit();
+ return;
+ }
+
+ register_core_service();
+
+ btp_send(btp, BTP_CORE_SERVICE, BTP_EV_CORE_READY,
+ BTP_INDEX_NON_CONTROLLER, 0, NULL);
+}
+
+static void usage(void)
+{
+ l_info("btpclient - Bluetooth tester");
+ l_info("Usage:");
+ l_info("\tbtpclient [options]");
+ l_info("options:\n"
+ "\t-s, --socket <socket> Socket to use for BTP\n"
+ "\t-q, --quiet Don't emit any logs\n"
+ "\t-v, --version Show version\n"
+ "\t-h, --help Show help options");
+}
+
+static const struct option options[] = {
+ { "socket", 1, 0, 's' },
+ { "quiet", 0, 0, 'q' },
+ { "version", 0, 0, 'v' },
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+};
+
+int main(int argc, char *argv[])
+{
+ struct l_dbus_client *client;
+ struct l_signal *signal;
+ struct l_dbus *dbus;
+ sigset_t mask;
+ int opt;
+
+ l_log_set_stderr();
+
+ while ((opt = getopt_long(argc, argv, "+hs:vq", options, NULL)) != -1) {
+ switch (opt) {
+ case 's':
+ socket_path = l_strdup(optarg);
+ break;
+ case 'q':
+ l_log_set_null();
+ break;
+ case 'd':
+ break;
+ case 'v':
+ l_info("%s", VERSION);
+ return EXIT_SUCCESS;
+ case 'h':
+ default:
+ usage();
+ return EXIT_SUCCESS;
+ }
+ }
+
+ if (!socket_path) {
+ l_info("Socket option is required");
+ l_info("Type --help for usage");
+ return EXIT_FAILURE;
+ }
+
+ if (!l_main_init())
+ return EXIT_FAILURE;
+
+
+ adapters = l_queue_new();
+
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGINT);
+ sigaddset(&mask, SIGTERM);
+ signal = l_signal_create(&mask, signal_handler, NULL, NULL);
+
+ dbus = l_dbus_new_default(L_DBUS_SYSTEM_BUS);
+ client = l_dbus_client_new(dbus, "org.bluez", "/org/bluez");
+
+ l_dbus_client_set_connect_handler(client, client_connected, NULL, NULL);
+ l_dbus_client_set_disconnect_handler(client, client_disconnected, NULL,
+ NULL);
+
+ l_dbus_client_set_proxy_handlers(client, proxy_added, proxy_removed,
+ property_changed, NULL, NULL);
+
+ l_dbus_client_set_ready_handler(client, client_ready, NULL, NULL);
+
+ l_main_run();
+
+ l_dbus_client_destroy(client);
+ l_dbus_destroy(dbus);
+ l_signal_remove(signal);
+ btp_cleanup(btp);
+
+ l_queue_destroy(adapters, (l_queue_destroy_func_t)btp_adapter_free);
+
+ l_free(socket_path);
+
+ l_main_exit();
+
+ return EXIT_SUCCESS;
+}
static bool client_active = false;
static bool debug_enabled = false;
static bool emulate_ecc = false;
+static bool skip_first_zero = false;
static void hexdump_print(const char *str, void *user_data)
{
uint8_t host_buf[4096];
uint16_t host_len;
bool host_shutdown;
+ bool host_skip_first_zero;
/* Receive events, ACL and SCO data */
int dev_fd;
util_hexdump('>', proxy->host_buf + proxy->host_len, len,
hexdump_print, "H: ");
+ if (proxy->host_skip_first_zero && len > 0) {
+ proxy->host_skip_first_zero = false;
+ if (proxy->host_buf[proxy->host_len] == '\0') {
+ printf("Skipping initial zero byte\n");
+ len--;
+ memmove(proxy->host_buf + proxy->host_len,
+ proxy->host_buf + proxy->host_len + 1, len);
+ }
+ }
+
proxy->host_len += len;
process_packet:
proxy->host_fd = host_fd;
proxy->host_shutdown = host_shutdown;
+ proxy->host_skip_first_zero = skip_first_zero;
proxy->dev_fd = dev_fd;
proxy->dev_shutdown = dev_shutdown;
static int open_unix(const char *path)
{
struct sockaddr_un addr;
+ size_t len;
int fd;
+ len = strlen(path);
+ if (len > sizeof(addr.sun_path) - 1) {
+ fprintf(stderr, "Path too long\n");
+ return -1;
+ }
+
unlink(path);
fd = socket(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
- strcpy(addr.sun_path, path);
+ strncpy(addr.sun_path, path, len);
if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
perror("Failed to bind Unix server socket");
for (;;) {
int opt;
- opt = getopt_long(argc, argv, "rc:l::u::p:i:aedvh",
+ opt = getopt_long(argc, argv, "rc:l::u::p:i:aezdvh",
main_options, NULL);
if (opt < 0)
break;
server_address = "0.0.0.0";
break;
case 'u':
- if (optarg)
+ if (optarg) {
+ struct sockaddr_un addr;
+
unix_path = optarg;
- else
+ if (strlen(unix_path) >
+ sizeof(addr.sun_path) - 1) {
+ fprintf(stderr, "Path too long\n");
+ return EXIT_FAILURE;
+ }
+ } else
unix_path = "/tmp/bt-server-bredr";
break;
case 'p':
case 'e':
emulate_ecc = true;
break;
+ case 'z':
+ skip_first_zero = true;
+ break;
case 'd':
debug_enabled = true;
break;
if (csr_map[i].id == id)
return csr_map[i].str;
- snprintf(str, 11, "Build %d", id);
+ snprintf(str, sizeof(str), "Build %d", id);
return str;
}
cmd.data[0] = 0x02; /* Field length */
cmd.data[1] = 0x01; /* Flags */
- cmd.data[2] |= 0x04; /* BR/EDR Not Supported */
+ cmd.data[2] = 0x04; /* BR/EDR Not Supported */
cmd.data[3] = 0x03; /* Field length */
cmd.data[4] = 0x03; /* 16-bit Service UUID list */
+++ /dev/null
-#!/usr/bin/python
-
-import dbus
-import dbus.exceptions
-import dbus.mainloop.glib
-import dbus.service
-
-import array
-import gobject
-
-from random import randint
-
-mainloop = None
-
-BLUEZ_SERVICE_NAME = 'org.bluez'
-GATT_MANAGER_IFACE = 'org.bluez.GattManager1'
-DBUS_OM_IFACE = 'org.freedesktop.DBus.ObjectManager'
-DBUS_PROP_IFACE = 'org.freedesktop.DBus.Properties'
-
-GATT_SERVICE_IFACE = 'org.bluez.GattService1'
-GATT_CHRC_IFACE = 'org.bluez.GattCharacteristic1'
-GATT_DESC_IFACE = 'org.bluez.GattDescriptor1'
-
-class InvalidArgsException(dbus.exceptions.DBusException):
- _dbus_error_name = 'org.freedesktop.DBus.Error.InvalidArgs'
-
-class NotSupportedException(dbus.exceptions.DBusException):
- _dbus_error_name = 'org.bluez.Error.NotSupported'
-
-class NotPermittedException(dbus.exceptions.DBusException):
- _dbus_error_name = 'org.bluez.Error.NotPermitted'
-
-class InvalidValueLengthException(dbus.exceptions.DBusException):
- _dbus_error_name = 'org.bluez.Error.InvalidValueLength'
-
-class FailedException(dbus.exceptions.DBusException):
- _dbus_error_name = 'org.bluez.Error.Failed'
-
-
-class Service(dbus.service.Object):
- PATH_BASE = '/org/bluez/example/service'
-
- def __init__(self, bus, index, uuid, primary):
- self.path = self.PATH_BASE + str(index)
- self.bus = bus
- self.uuid = uuid
- self.primary = primary
- self.characteristics = []
- dbus.service.Object.__init__(self, bus, self.path)
-
- def get_properties(self):
- return {
- GATT_SERVICE_IFACE: {
- 'UUID': self.uuid,
- 'Primary': self.primary,
- 'Characteristics': dbus.Array(
- self.get_characteristic_paths(),
- signature='o')
- }
- }
-
- def get_path(self):
- return dbus.ObjectPath(self.path)
-
- def add_characteristic(self, characteristic):
- self.characteristics.append(characteristic)
-
- def get_characteristic_paths(self):
- result = []
- for chrc in self.characteristics:
- result.append(chrc.get_path())
- return result
-
- def get_characteristics(self):
- return self.characteristics
-
- @dbus.service.method(DBUS_PROP_IFACE,
- in_signature='s',
- out_signature='a{sv}')
- def GetAll(self, interface):
- if interface != GATT_SERVICE_IFACE:
- raise InvalidArgsException()
-
- return self.get_properties[GATT_SERVICE_IFACE]
-
- @dbus.service.method(DBUS_OM_IFACE, out_signature='a{oa{sa{sv}}}')
- def GetManagedObjects(self):
- response = {}
- print 'GetManagedObjects'
-
- response[self.get_path()] = self.get_properties()
- chrcs = self.get_characteristics()
- for chrc in chrcs:
- response[chrc.get_path()] = chrc.get_properties()
- descs = chrc.get_descriptors()
- for desc in descs:
- response[desc.get_path()] = desc.get_properties()
-
- return response
-
-
-class Characteristic(dbus.service.Object):
- def __init__(self, bus, index, uuid, flags, service):
- self.path = service.path + '/char' + str(index)
- self.bus = bus
- self.uuid = uuid
- self.service = service
- self.flags = flags
- self.descriptors = []
- dbus.service.Object.__init__(self, bus, self.path)
-
- def get_properties(self):
- return {
- GATT_CHRC_IFACE: {
- 'Service': self.service.get_path(),
- 'UUID': self.uuid,
- 'Flags': self.flags,
- 'Descriptors': dbus.Array(
- self.get_descriptor_paths(),
- signature='o')
- }
- }
-
- def get_path(self):
- return dbus.ObjectPath(self.path)
-
- def add_descriptor(self, descriptor):
- self.descriptors.append(descriptor)
-
- def get_descriptor_paths(self):
- result = []
- for desc in self.descriptors:
- result.append(desc.get_path())
- return result
-
- def get_descriptors(self):
- return self.descriptors
-
- @dbus.service.method(DBUS_PROP_IFACE,
- in_signature='s',
- out_signature='a{sv}')
- def GetAll(self, interface):
- if interface != GATT_CHRC_IFACE:
- raise InvalidArgsException()
-
- return self.get_properties[GATT_CHRC_IFACE]
-
- @dbus.service.method(GATT_CHRC_IFACE, out_signature='ay')
- def ReadValue(self):
- print 'Default ReadValue called, returning error'
- raise NotSupportedException()
-
- @dbus.service.method(GATT_CHRC_IFACE, in_signature='ay')
- def WriteValue(self, value):
- print 'Default WriteValue called, returning error'
- raise NotSupportedException()
-
- @dbus.service.method(GATT_CHRC_IFACE)
- def StartNotify(self):
- print 'Default StartNotify called, returning error'
- raise NotSupportedException()
-
- @dbus.service.method(GATT_CHRC_IFACE)
- def StopNotify(self):
- print 'Default StopNotify called, returning error'
- raise NotSupportedException()
-
- @dbus.service.signal(DBUS_PROP_IFACE,
- signature='sa{sv}as')
- def PropertiesChanged(self, interface, changed, invalidated):
- pass
-
-
-class Descriptor(dbus.service.Object):
- def __init__(self, bus, index, uuid, characteristic):
- self.path = characteristic.path + '/desc' + str(index)
- self.bus = bus
- self.uuid = uuid
- self.chrc = characteristic
- dbus.service.Object.__init__(self, bus, self.path)
-
- def get_properties(self):
- return {
- GATT_DESC_IFACE: {
- 'Characteristic': self.chrc.get_path(),
- 'UUID': self.uuid,
- }
- }
-
- def get_path(self):
- return dbus.ObjectPath(self.path)
-
- @dbus.service.method(DBUS_PROP_IFACE,
- in_signature='s',
- out_signature='a{sv}')
- def GetAll(self, interface):
- if interface != GATT_DESC_IFACE:
- raise InvalidArgsException()
-
- return self.get_properties[GATT_CHRC_IFACE]
-
- @dbus.service.method(GATT_DESC_IFACE, out_signature='ay')
- def ReadValue(self):
- print 'Default ReadValue called, returning error'
- raise NotSupportedException()
-
- @dbus.service.method(GATT_DESC_IFACE, in_signature='ay')
- def WriteValue(self, value):
- print 'Default WriteValue called, returning error'
- raise NotSupportedException()
-
-
-class HeartRateService(Service):
- """
- Fake Heart Rate Service that simulates a fake heart beat and control point
- behavior.
-
- """
- HR_UUID = '0000180d-0000-1000-8000-00805f9b34fb'
-
- def __init__(self, bus, index):
- Service.__init__(self, bus, index, self.HR_UUID, True)
- self.add_characteristic(HeartRateMeasurementChrc(bus, 0, self))
- self.add_characteristic(BodySensorLocationChrc(bus, 1, self))
- self.add_characteristic(HeartRateControlPointChrc(bus, 2, self))
- self.energy_expended = 0
-
-
-class HeartRateMeasurementChrc(Characteristic):
- HR_MSRMT_UUID = '00002a37-0000-1000-8000-00805f9b34fb'
-
- def __init__(self, bus, index, service):
- Characteristic.__init__(
- self, bus, index,
- self.HR_MSRMT_UUID,
- ['notify'],
- service)
- self.notifying = False
- self.hr_ee_count = 0
-
- def hr_msrmt_cb(self):
- value = []
- value.append(dbus.Byte(0x06))
-
- value.append(dbus.Byte(randint(90, 130)))
-
- if self.hr_ee_count % 10 == 0:
- value[0] = dbus.Byte(value[0] | 0x08)
- value.append(dbus.Byte(self.service.energy_expended & 0xff))
- value.append(dbus.Byte((self.service.energy_expended >> 8) & 0xff))
-
- self.service.energy_expended = \
- min(0xffff, self.service.energy_expended + 1)
- self.hr_ee_count += 1
-
- print 'Updating value: ' + repr(value)
-
- self.PropertiesChanged(GATT_CHRC_IFACE, { 'Value': value }, [])
-
- return self.notifying
-
- def _update_hr_msrmt_simulation(self):
- print 'Update HR Measurement Simulation'
-
- if not self.notifying:
- return
-
- gobject.timeout_add(1000, self.hr_msrmt_cb)
-
- def StartNotify(self):
- if self.notifying:
- print 'Already notifying, nothing to do'
- return
-
- self.notifying = True
- self._update_hr_msrmt_simulation()
-
- def StopNotify(self):
- if not self.notifying:
- print 'Not notifying, nothing to do'
- return
-
- self.notifying = False
- self._update_hr_msrmt_simulation()
-
-
-class BodySensorLocationChrc(Characteristic):
- BODY_SNSR_LOC_UUID = '00002a38-0000-1000-8000-00805f9b34fb'
-
- def __init__(self, bus, index, service):
- Characteristic.__init__(
- self, bus, index,
- self.BODY_SNSR_LOC_UUID,
- ['read'],
- service)
-
- def ReadValue(self):
- # Return 'Chest' as the sensor location.
- return [ 0x01 ]
-
-class HeartRateControlPointChrc(Characteristic):
- HR_CTRL_PT_UUID = '00002a39-0000-1000-8000-00805f9b34fb'
-
- def __init__(self, bus, index, service):
- Characteristic.__init__(
- self, bus, index,
- self.HR_CTRL_PT_UUID,
- ['write'],
- service)
-
- def WriteValue(self, value):
- print 'Heart Rate Control Point WriteValue called'
-
- if len(value) != 1:
- raise InvalidValueLengthException()
-
- byte = value[0]
- print 'Control Point value: ' + repr(byte)
-
- if byte != 1:
- raise FailedException("0x80")
-
- print 'Energy Expended field reset!'
- self.service.energy_expended = 0
-
-
-class BatteryService(Service):
- """
- Fake Battery service that emulates a draining battery.
-
- """
- BATTERY_UUID = '180f'
-
- def __init__(self, bus, index):
- Service.__init__(self, bus, index, self.BATTERY_UUID, True)
- self.add_characteristic(BatteryLevelCharacteristic(bus, 0, self))
-
-
-class BatteryLevelCharacteristic(Characteristic):
- """
- Fake Battery Level characteristic. The battery level is drained by 2 points
- every 5 seconds.
-
- """
- BATTERY_LVL_UUID = '2a19'
-
- def __init__(self, bus, index, service):
- Characteristic.__init__(
- self, bus, index,
- self.BATTERY_LVL_UUID,
- ['read', 'notify'],
- service)
- self.notifying = False
- self.battery_lvl = 100
- gobject.timeout_add(5000, self.drain_battery)
-
- def notify_battery_level(self):
- if not self.notifying:
- return
- self.PropertiesChanged(
- GATT_CHRC_IFACE,
- { 'Value': [dbus.Byte(self.battery_lvl)] }, [])
-
- def drain_battery(self):
- if self.battery_lvl > 0:
- self.battery_lvl -= 2
- if self.battery_lvl < 0:
- self.battery_lvl = 0
- print 'Battery Level drained: ' + repr(self.battery_lvl)
- self.notify_battery_level()
- return True
-
- def ReadValue(self):
- print 'Battery Level read: ' + repr(self.battery_lvl)
- return [dbus.Byte(self.battery_lvl)]
-
- def StartNotify(self):
- if self.notifying:
- print 'Already notifying, nothing to do'
- return
-
- self.notifying = True
- self.notify_battery_level()
-
- def StopNotify(self):
- if not self.notifying:
- print 'Not notifying, nothing to do'
- return
-
- self.notifying = False
-
-
-class TestService(Service):
- """
- Dummy test service that provides characteristics and descriptors that
- exercise various API functionality.
-
- """
- TEST_SVC_UUID = '12345678-1234-5678-1234-56789abcdef0'
-
- def __init__(self, bus, index):
- Service.__init__(self, bus, index, self.TEST_SVC_UUID, False)
- self.add_characteristic(TestCharacteristic(bus, 0, self))
-
-
-class TestCharacteristic(Characteristic):
- """
- Dummy test characteristic. Allows writing arbitrary bytes to its value, and
- contains "extended properties", as well as a test descriptor.
-
- """
- TEST_CHRC_UUID = '12345678-1234-5678-1234-56789abcdef1'
-
- def __init__(self, bus, index, service):
- Characteristic.__init__(
- self, bus, index,
- self.TEST_CHRC_UUID,
- ['read', 'write', 'writable-auxiliaries'],
- service)
- self.value = []
- self.add_descriptor(TestDescriptor(bus, 0, self))
- self.add_descriptor(
- CharacteristicUserDescriptionDescriptor(bus, 1, self))
-
- def ReadValue(self):
- print 'TestCharacteristic Read: ' + repr(self.value)
- return self.value
-
- def WriteValue(self, value):
- print 'TestCharacteristic Write: ' + repr(value)
- self.value = value
-
-
-class TestDescriptor(Descriptor):
- """
- Dummy test descriptor. Returns a static value.
-
- """
- TEST_DESC_UUID = '12345678-1234-5678-1234-56789abcdef2'
-
- def __init__(self, bus, index, characteristic):
- Descriptor.__init__(
- self, bus, index,
- self.TEST_DESC_UUID,
- characteristic)
-
- def ReadValue(self):
- return [
- dbus.Byte('T'), dbus.Byte('e'), dbus.Byte('s'), dbus.Byte('t')
- ]
-
-
-class CharacteristicUserDescriptionDescriptor(Descriptor):
- """
- Writable CUD descriptor.
-
- """
- CUD_UUID = '2901'
-
- def __init__(self, bus, index, characteristic):
- self.writable = 'writable-auxiliaries' in characteristic.flags
- self.value = array.array('B', 'This is a characteristic for testing')
- self.value = self.value.tolist()
- Descriptor.__init__(
- self, bus, index,
- self.CUD_UUID,
- characteristic)
-
- def ReadValue(self):
- return self.value
-
- def WriteValue(self, value):
- if not self.writable:
- raise NotPermittedException()
- self.value = value
-
-
-def register_service_cb():
- print 'GATT service registered'
-
-
-def register_service_error_cb(error):
- print 'Failed to register service: ' + str(error)
- mainloop.quit()
-
-
-def find_adapter(bus):
- remote_om = dbus.Interface(bus.get_object(BLUEZ_SERVICE_NAME, '/'),
- DBUS_OM_IFACE)
- objects = remote_om.GetManagedObjects()
-
- for o, props in objects.iteritems():
- if props.has_key(GATT_MANAGER_IFACE):
- return o
-
- return None
-
-def main():
- global mainloop
-
- dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
-
- bus = dbus.SystemBus()
-
- adapter = find_adapter(bus)
- if not adapter:
- print 'GattManager1 interface not found'
- return
-
- service_manager = dbus.Interface(
- bus.get_object(BLUEZ_SERVICE_NAME, adapter),
- GATT_MANAGER_IFACE)
-
- hr_service = HeartRateService(bus, 0)
- bat_service = BatteryService(bus, 1)
- test_service = TestService(bus, 2)
-
- mainloop = gobject.MainLoop()
-
- service_manager.RegisterService(hr_service.get_path(), {},
- reply_handler=register_service_cb,
- error_handler=register_service_error_cb)
- service_manager.RegisterService(bat_service.get_path(), {},
- reply_handler=register_service_cb,
- error_handler=register_service_error_cb)
- service_manager.RegisterService(test_service.get_path(), {},
- reply_handler=register_service_cb,
- error_handler=register_service_error_cb)
-
- mainloop.run()
-
-if __name__ == '__main__':
- main()
uint8_t status = *((uint8_t *) data);
if (status) {
- tester_warn("Failed to send DHKey gen cmd (0x%02x)", status);
+ tester_warn("Failed to send Read Local PK256 cmd (0x%02x)", status);
tester_test_failed();
return;
}
{
DIR *dir;
struct dirent *d;
- char pathname[PATH_MAX], filename[NAME_MAX], prefix[20];
+ char pathname[PATH_MAX], filename[NAME_MAX + 2], prefix[20];
unsigned char cmd[256];
unsigned char buf[256];
uint8_t seqnum = 0;
cmd.data[0] = 0x02; /* Field length */
cmd.data[1] = 0x01; /* Flags */
- cmd.data[2] = 0x02; /* LE General Discoverable Mode */
- cmd.data[2] |= 0x04; /* BR/EDR Not Supported */
+ cmd.data[2] = 0x04; /* BR/EDR Not Supported */
cmd.data[3] = 0x1a; /* Field length */
cmd.data[4] = 0xff; /* Vendor field */
user->hciemu_type = HCIEMU_TYPE_BREDRLE; \
user->test_setup = setup; \
user->test_data = data; \
- user->expected_version = 0x08; \
+ user->expected_version = 0x09; \
user->expected_manufacturer = 0x003f; \
user->expected_supported_settings = 0x0000bfff; \
user->initial_settings = 0x00000080; \
user->hciemu_type = HCIEMU_TYPE_LE; \
user->test_setup = setup; \
user->test_data = data; \
- user->expected_version = 0x08; \
+ user->expected_version = 0x09; \
user->expected_manufacturer = 0x003f; \
user->expected_supported_settings = 0x0000be1b; \
user->initial_settings = 0x00000200; \
static const char ext_ctrl_info1[] = {
0x00, 0x00, 0x00, 0x01, 0xaa, 0x00, /* btaddr */
- 0x08, /* version */
+ 0x09, /* version */
0x3f, 0x00, /* manufacturer */
0xff, 0xbf, 0x00, 0x00, /* supported settings */
0x80, 0x00, 0x00, 0x00, /* current settings */
static const char ext_ctrl_info2[] = {
0x00, 0x00, 0x00, 0x01, 0xaa, 0x00, /* btaddr */
- 0x08, /* version */
+ 0x09, /* version */
0x3f, 0x00, /* manufacturer */
0xff, 0xbf, 0x00, 0x00, /* supported settings */
0x81, 0x02, 0x00, 0x00, /* current settings */
static const char ext_ctrl_info3[] = {
0x00, 0x00, 0x00, 0x01, 0xaa, 0x00, /* btaddr */
- 0x08, /* version */
+ 0x09, /* version */
0x3f, 0x00, /* manufacturer */
0xff, 0xbf, 0x00, 0x00, /* supported settings */
0x80, 0x02, 0x00, 0x00, /* current settings */
static const char ext_ctrl_info4[] = {
0x00, 0x00, 0x00, 0x01, 0xaa, 0x00, /* btaddr */
- 0x08, /* version */
+ 0x09, /* version */
0x3f, 0x00, /* manufacturer */
0xff, 0xbf, 0x00, 0x00, /* supported settings */
0x80, 0x02, 0x00, 0x00, /* current settings */
static const char ext_ctrl_info5[] = {
0x00, 0x00, 0x00, 0x01, 0xaa, 0x00, /* btaddr */
- 0x08, /* version */
+ 0x09, /* version */
0x3f, 0x00, /* manufacturer */
0xff, 0xbf, 0x00, 0x00, /* supported settings */
0x81, 0x02, 0x00, 0x00, /* current settings */
return;
}
- add_history(line_read);
+ if (history_search(line_read, -1))
+ add_history(line_read);
g_shell_parse_argv(line_read, &argcp, &argvp, NULL);
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
-#include <signal.h>
-#include <sys/signalfd.h>
+#include <string.h>
#include <inttypes.h>
#include <wordexp.h>
+#include <ctype.h>
-#include <readline/readline.h>
-#include <readline/history.h>
#include <glib.h>
#include "gdbus/gdbus.h"
-#include "client/display.h"
+#include "src/shared/shell.h"
/* String display constants */
#define COLORED_NEW COLOR_GREEN "NEW" COLOR_OFF
#define OBEX_MAP_INTERFACE "org.bluez.obex.MessageAccess1"
#define OBEX_MSG_INTERFACE "org.bluez.obex.Message1"
-static GMainLoop *main_loop;
static DBusConnection *dbus_conn;
static GDBusProxy *default_session;
static GSList *sessions = NULL;
static void connect_handler(DBusConnection *connection, void *user_data)
{
- rl_set_prompt(PROMPT_ON);
- printf("\r");
- rl_on_new_line();
- rl_redisplay();
+ bt_shell_set_prompt(PROMPT_ON);
}
static void disconnect_handler(DBusConnection *connection, void *user_data)
{
- rl_set_prompt(PROMPT_OFF);
- printf("\r");
- rl_on_new_line();
- rl_redisplay();
-}
-
-static void cmd_quit(int argc, char *argv[])
-{
- g_main_loop_quit(main_loop);
+ bt_shell_set_prompt(PROMPT_OFF);
}
static void connect_reply(DBusMessage *message, void *user_data)
dbus_error_init(&error);
if (dbus_set_error_from_message(&error, message) == TRUE) {
- rl_printf("Failed to connect: %s\n", error.name);
+ bt_shell_printf("Failed to connect: %s\n", error.name);
dbus_error_free(&error);
return;
}
- rl_printf("Connection successful\n");
+ bt_shell_printf("Connection successful\n");
}
static void append_variant(DBusMessageIter *iter, int type, void *val)
struct connect_args *args;
const char *target = "opp";
- if (argc < 2) {
- rl_printf("Missing device address argument\n");
- return;
- }
-
if (!client) {
- rl_printf("Client proxy not available\n");
+ bt_shell_printf("Client proxy not available\n");
return;
}
if (g_dbus_proxy_method_call(client, "CreateSession", connect_setup,
connect_reply, args, connect_args_free) == FALSE) {
- rl_printf("Failed to connect\n");
+ bt_shell_printf("Failed to connect\n");
return;
}
- rl_printf("Attempting to connect to %s\n", argv[1]);
+ bt_shell_printf("Attempting to connect to %s\n", argv[1]);
}
static void disconnect_reply(DBusMessage *message, void *user_data)
dbus_error_init(&error);
if (dbus_set_error_from_message(&error, message) == TRUE) {
- rl_printf("Failed to disconnect: %s\n", error.name);
+ bt_shell_printf("Failed to disconnect: %s\n", error.name);
dbus_error_free(&error);
return;
}
- rl_printf("Disconnection successful\n");
+ bt_shell_printf("Disconnection successful\n");
}
static void disconnect_setup(DBusMessageIter *iter, void *user_data)
proxy = default_session;
if (proxy == NULL) {
- rl_printf("Session not available\n");
+ bt_shell_printf("Session not available\n");
return;
}
if (g_dbus_proxy_method_call(client, "RemoveSession", disconnect_setup,
disconnect_reply, proxy, NULL) == FALSE) {
- rl_printf("Failed to disconnect\n");
+ bt_shell_printf("Failed to disconnect\n");
return;
}
- rl_printf("Attempting to disconnect to %s\n",
+ bt_shell_printf("Attempting to disconnect to %s\n",
g_dbus_proxy_get_path(proxy));
}
str = proxy_description(proxy, title, description);
- rl_printf("%s%s\n", str, default_session == proxy ? "[default]" : "");
+ bt_shell_printf("%s%s\n", str, default_session == proxy ? "[default]" : "");
g_free(str);
}
static bool check_default_session(void)
{
if (!default_session) {
- rl_printf("No default session available\n");
+ bt_shell_printf("No default session available\n");
return FALSE;
}
DBusMessageIter subiter;
if (iter == NULL) {
- rl_printf("%s%s is nil\n", label, name);
+ bt_shell_printf("%s%s is nil\n", label, name);
return;
}
switch (dbus_message_iter_get_arg_type(iter)) {
case DBUS_TYPE_INVALID:
- rl_printf("%s%s is invalid\n", label, name);
+ bt_shell_printf("%s%s is invalid\n", label, name);
break;
case DBUS_TYPE_STRING:
case DBUS_TYPE_OBJECT_PATH:
dbus_message_iter_get_basic(iter, &valstr);
- rl_printf("%s%s: %s\n", label, name, valstr);
+ bt_shell_printf("%s%s: %s\n", label, name, valstr);
break;
case DBUS_TYPE_BOOLEAN:
dbus_message_iter_get_basic(iter, &valbool);
- rl_printf("%s%s: %s\n", label, name,
+ bt_shell_printf("%s%s: %s\n", label, name,
valbool == TRUE ? "yes" : "no");
break;
case DBUS_TYPE_UINT64:
dbus_message_iter_get_basic(iter, &valu64);
- rl_printf("%s%s: %" PRIu64 "\n", label, name, valu64);
+ bt_shell_printf("%s%s: %" PRIu64 "\n", label, name, valu64);
break;
case DBUS_TYPE_UINT32:
dbus_message_iter_get_basic(iter, &valu32);
- rl_printf("%s%s: 0x%08x\n", label, name, valu32);
+ bt_shell_printf("%s%s: 0x%08x\n", label, name, valu32);
break;
case DBUS_TYPE_UINT16:
dbus_message_iter_get_basic(iter, &valu16);
- rl_printf("%s%s: 0x%04x\n", label, name, valu16);
+ bt_shell_printf("%s%s: 0x%04x\n", label, name, valu16);
break;
case DBUS_TYPE_INT16:
dbus_message_iter_get_basic(iter, &vals16);
- rl_printf("%s%s: %d\n", label, name, vals16);
+ bt_shell_printf("%s%s: %d\n", label, name, vals16);
break;
case DBUS_TYPE_VARIANT:
dbus_message_iter_recurse(iter, &subiter);
print_iter(label, valstr, &subiter);
break;
default:
- rl_printf("%s%s has unsupported type\n", label, name);
+ bt_shell_printf("%s%s has unsupported type\n", label, name);
break;
}
}
} else {
proxy = find_session(argv[1]);
if (!proxy) {
- rl_printf("Session %s not available\n", argv[1]);
+ bt_shell_printf("Session %s not available\n", argv[1]);
return;
}
}
- rl_printf("Session %s\n", g_dbus_proxy_get_path(proxy));
+ bt_shell_printf("Session %s\n", g_dbus_proxy_get_path(proxy));
print_property(proxy, "Destination");
print_property(proxy, "Target");
desc = g_strdup_printf(COLOR_BLUE "[%s]" COLOR_OFF "# ", desc);
done:
- rl_set_prompt(desc);
- rl_redisplay();
+ bt_shell_set_prompt(desc);
g_free(desc);
}
{
GDBusProxy *proxy;
- if (argc < 2) {
- rl_printf("Missing session address argument\n");
- return;
- }
-
proxy = find_session(argv[1]);
if (proxy == NULL) {
- rl_printf("Session %s not available\n", argv[1]);
+ bt_shell_printf("Session %s not available\n", argv[1]);
return;
}
static void transfer_info(GDBusProxy *proxy, int argc, char *argv[])
{
- rl_printf("Transfer %s\n", g_dbus_proxy_get_path(proxy));
+ bt_shell_printf("Transfer %s\n", g_dbus_proxy_get_path(proxy));
print_property(proxy, "Session");
print_property(proxy, "Name");
static void message_info(GDBusProxy *proxy, int argc, char *argv[])
{
- rl_printf("Message %s\n", g_dbus_proxy_get_path(proxy));
+ bt_shell_printf("Message %s\n", g_dbus_proxy_get_path(proxy));
print_property(proxy, "Folder");
print_property(proxy, "Subject");
{
GDBusProxy *proxy;
- if (argc < 2) {
- rl_printf("Missing object path argument\n");
- return;
- }
-
proxy = find_transfer(argv[1]);
if (proxy) {
transfer_info(proxy, argc, argv);
return;
}
- rl_printf("Object %s not available\n", argv[1]);
+ bt_shell_printf("Object %s not available\n", argv[1]);
}
static void cancel_reply(DBusMessage *message, void *user_data)
dbus_error_init(&error);
if (dbus_set_error_from_message(&error, message) == TRUE) {
- rl_printf("Failed to cancel: %s\n", error.name);
+ bt_shell_printf("Failed to cancel: %s\n", error.name);
dbus_error_free(&error);
return;
}
- rl_printf("Cancel successful\n");
+ bt_shell_printf("Cancel successful\n");
}
static void cmd_cancel(int argc, char *argv[])
{
GDBusProxy *proxy;
- if (argc < 2) {
- rl_printf("Missing transfer address argument\n");
- return;
- }
-
proxy = find_transfer(argv[1]);
if (!proxy) {
- rl_printf("Transfer %s not available\n", argv[1]);
+ bt_shell_printf("Transfer %s not available\n", argv[1]);
return;
}
if (g_dbus_proxy_method_call(proxy, "Cancel", NULL, cancel_reply, NULL,
NULL) == FALSE) {
- rl_printf("Failed to cancel transfer\n");
+ bt_shell_printf("Failed to cancel transfer\n");
return;
}
- rl_printf("Attempting to cancel transfer %s\n",
+ bt_shell_printf("Attempting to cancel transfer %s\n",
g_dbus_proxy_get_path(proxy));
}
dbus_error_init(&error);
if (dbus_set_error_from_message(&error, message) == TRUE) {
- rl_printf("Failed to suspend: %s\n", error.name);
+ bt_shell_printf("Failed to suspend: %s\n", error.name);
dbus_error_free(&error);
return;
}
- rl_printf("Suspend successful\n");
+ bt_shell_printf("Suspend successful\n");
}
static void cmd_suspend(int argc, char *argv[])
{
GDBusProxy *proxy;
- if (argc < 2) {
- rl_printf("Missing transfer address argument\n");
- return;
- }
-
proxy = find_transfer(argv[1]);
if (!proxy) {
- rl_printf("Transfer %s not available\n", argv[1]);
+ bt_shell_printf("Transfer %s not available\n", argv[1]);
return;
}
if (g_dbus_proxy_method_call(proxy, "Suspend", NULL, suspend_reply,
NULL, NULL) == FALSE) {
- rl_printf("Failed to suspend transfer\n");
+ bt_shell_printf("Failed to suspend transfer\n");
return;
}
- rl_printf("Attempting to suspend transfer %s\n",
+ bt_shell_printf("Attempting to suspend transfer %s\n",
g_dbus_proxy_get_path(proxy));
}
dbus_error_init(&error);
if (dbus_set_error_from_message(&error, message) == TRUE) {
- rl_printf("Failed to resume: %s\n", error.name);
+ bt_shell_printf("Failed to resume: %s\n", error.name);
dbus_error_free(&error);
return;
}
- rl_printf("Resume successful\n");
+ bt_shell_printf("Resume successful\n");
}
static void cmd_resume(int argc, char *argv[])
{
GDBusProxy *proxy;
- if (argc < 2) {
- rl_printf("Missing transfer address argument\n");
- return;
- }
-
proxy = find_transfer(argv[1]);
if (!proxy) {
- rl_printf("Transfer %s not available\n", argv[1]);
+ bt_shell_printf("Transfer %s not available\n", argv[1]);
return;
}
if (g_dbus_proxy_method_call(proxy, "Resume", NULL, resume_reply,
NULL, NULL) == FALSE) {
- rl_printf("Failed to resume transfer\n");
+ bt_shell_printf("Failed to resume transfer\n");
return;
}
- rl_printf("Attempting to resume transfer %s\n",
+ bt_shell_printf("Attempting to resume transfer %s\n",
g_dbus_proxy_get_path(proxy));
}
dbus_message_iter_get_basic(iter, &path);
- rl_printf("Transfer %s\n", path);
+ bt_shell_printf("Transfer %s\n", path);
dbus_message_iter_next(iter);
dbus_error_init(&error);
if (dbus_set_error_from_message(&error, message) == TRUE) {
- rl_printf("Failed to send/pull: %s\n", error.name);
+ bt_shell_printf("Failed to send/pull: %s\n", error.name);
dbus_error_free(&error);
return;
}
static void opp_send(GDBusProxy *proxy, int argc, char *argv[])
{
- if (argc < 2) {
- rl_printf("Missing file argument\n");
- return;
- }
-
if (g_dbus_proxy_method_call(proxy, "SendFile", send_setup, send_reply,
g_strdup(argv[1]), g_free) == FALSE) {
- rl_printf("Failed to send\n");
+ bt_shell_printf("Failed to send\n");
return;
}
- rl_printf("Attempting to send %s to %s\n", argv[1],
+ bt_shell_printf("Attempting to send %s to %s\n", argv[1],
g_dbus_proxy_get_path(proxy));
}
static void opp_pull(GDBusProxy *proxy, int argc, char *argv[])
{
- if (argc < 2) {
- rl_printf("Missing file argument\n");
- return;
- }
-
if (g_dbus_proxy_method_call(proxy, "PullBusinessCard", send_setup,
send_reply, g_strdup(argv[1]), g_free) == FALSE) {
- rl_printf("Failed to pull\n");
+ bt_shell_printf("Failed to pull\n");
return;
}
- rl_printf("Attempting to pull %s from %s\n", argv[1],
+ bt_shell_printf("Attempting to pull %s from %s\n", argv[1],
g_dbus_proxy_get_path(proxy));
}
dbus_error_init(&error);
if (dbus_set_error_from_message(&error, message) == TRUE) {
- rl_printf("Failed to PushMessage: %s\n", error.name);
+ bt_shell_printf("Failed to PushMessage: %s\n", error.name);
dbus_error_free(&error);
return;
}
static void map_send(GDBusProxy *proxy, int argc, char *argv[])
{
- if (argc < 2) {
- rl_printf("Missing file argument\n");
- return;
- }
-
if (g_dbus_proxy_method_call(proxy, "PushMessage", push_setup,
push_reply, g_strdup(argv[1]),
g_free) == FALSE) {
- rl_printf("Failed to send\n");
+ bt_shell_printf("Failed to send\n");
return;
}
- rl_printf("Attempting to send %s to %s\n", argv[1],
+ bt_shell_printf("Attempting to send %s to %s\n", argv[1],
g_dbus_proxy_get_path(proxy));
}
return;
}
- rl_printf("Command not supported\n");
+ bt_shell_printf("Command not supported\n");
}
static void cmd_pull(int argc, char *argv[])
return;
}
- rl_printf("Command not supported\n");
+ bt_shell_printf("Command not supported\n");
}
static void change_folder_reply(DBusMessage *message, void *user_data)
dbus_error_init(&error);
if (dbus_set_error_from_message(&error, message) == TRUE) {
- rl_printf("Failed to ChangeFolder: %s\n", error.name);
+ bt_shell_printf("Failed to ChangeFolder: %s\n", error.name);
dbus_error_free(&error);
return;
}
- rl_printf("ChangeFolder successful\n");
+ bt_shell_printf("ChangeFolder successful\n");
}
static void change_folder_setup(DBusMessageIter *iter, void *user_data)
dbus_error_init(&error);
if (dbus_set_error_from_message(&error, message) == TRUE) {
- rl_printf("Failed to Select: %s\n", error.name);
+ bt_shell_printf("Failed to Select: %s\n", error.name);
dbus_error_free(&error);
return;
}
dbus_message_iter_init(message, &iter);
- rl_printf("Select successful\n");
+ bt_shell_printf("Select successful\n");
}
static void select_setup(DBusMessageIter *iter, void *user_data)
dbus_error_init(&error);
if (dbus_set_error_from_message(&error, message) == TRUE) {
- rl_printf("Failed to SetFolder: %s\n", error.name);
+ bt_shell_printf("Failed to SetFolder: %s\n", error.name);
dbus_error_free(&error);
return;
}
- rl_printf("SetFolder successful\n");
+ bt_shell_printf("SetFolder successful\n");
}
static void setfolder_setup(DBusMessageIter *iter, void *user_data)
static void ftp_cd(GDBusProxy *proxy, int argc, char *argv[])
{
- if (argc < 2) {
- rl_printf("Missing path argument\n");
- return;
- }
-
if (g_dbus_proxy_method_call(proxy, "ChangeFolder", change_folder_setup,
change_folder_reply, g_strdup(argv[1]),
g_free) == FALSE) {
- rl_printf("Failed to ChangeFolder\n");
+ bt_shell_printf("Failed to ChangeFolder\n");
return;
}
- rl_printf("Attempting to ChangeFolder to %s\n", argv[1]);
+ bt_shell_printf("Attempting to ChangeFolder to %s\n", argv[1]);
}
static void pbap_cd(GDBusProxy *proxy, int argc, char *argv[])
{
- if (argc < 2) {
- rl_printf("Missing path argument\n");
- return;
- }
-
if (g_dbus_proxy_method_call(proxy, "Select", select_setup,
select_reply, g_strdup(argv[1]),
g_free) == FALSE) {
- rl_printf("Failed to Select\n");
+ bt_shell_printf("Failed to Select\n");
return;
}
- rl_printf("Attempting to Select to %s\n", argv[1]);
+ bt_shell_printf("Attempting to Select to %s\n", argv[1]);
}
static void map_cd(GDBusProxy *proxy, int argc, char *argv[])
{
- if (argc < 2) {
- rl_printf("Missing path argument\n");
- return;
- }
-
if (g_dbus_proxy_method_call(proxy, "SetFolder", setfolder_setup,
setfolder_reply, g_strdup(argv[1]),
g_free) == FALSE) {
- rl_printf("Failed to SetFolder\n");
+ bt_shell_printf("Failed to SetFolder\n");
return;
}
- rl_printf("Attempting to SetFolder to %s\n", argv[1]);
+ bt_shell_printf("Attempting to SetFolder to %s\n", argv[1]);
}
static void cmd_cd(int argc, char *argv[])
return;
}
- rl_printf("Command not supported\n");
+ bt_shell_printf("Command not supported\n");
}
static void list_folder_reply(DBusMessage *message, void *user_data)
dbus_error_init(&error);
if (dbus_set_error_from_message(&error, message) == TRUE) {
- rl_printf("Failed to ListFolder: %s\n", error.name);
+ bt_shell_printf("Failed to ListFolder: %s\n", error.name);
dbus_error_free(&error);
return;
}
if (g_dbus_proxy_method_call(proxy, "ListFolder", NULL,
list_folder_reply, NULL,
NULL) == FALSE) {
- rl_printf("Failed to ls\n");
+ bt_shell_printf("Failed to ls\n");
return;
}
- rl_printf("Attempting to ListFolder\n");
+ bt_shell_printf("Attempting to ListFolder\n");
}
static void parse_list_reply(DBusMessage *message)
dbus_error_init(&error);
if (dbus_set_error_from_message(&error, message) == TRUE) {
- rl_printf("Failed to List: %s\n", error.name);
+ bt_shell_printf("Failed to List: %s\n", error.name);
dbus_error_free(&error);
return;
}
dbus_error_init(&error);
if (dbus_set_error_from_message(&error, message) == TRUE) {
- rl_printf("Failed to Search: %s\n", error.name);
+ bt_shell_printf("Failed to Search: %s\n", error.name);
dbus_error_free(&error);
return;
}
if (g_dbus_proxy_method_call(proxy, "Search", search_setup,
search_reply, g_strdup(argv[1]),
g_free) == FALSE) {
- rl_printf("Failed to Search\n");
+ bt_shell_printf("Failed to Search\n");
return;
}
- rl_printf("Attempting to Search\n");
+ bt_shell_printf("Attempting to Search\n");
}
static void list_folders_reply(DBusMessage *message, void *user_data)
dbus_error_init(&error);
if (dbus_set_error_from_message(&error, message) == TRUE) {
- rl_printf("Failed to ListFolders: %s\n", error.name);
+ bt_shell_printf("Failed to ListFolders: %s\n", error.name);
dbus_error_free(&error);
return;
}
dbus_error_init(&error);
if (dbus_set_error_from_message(&error, message) == TRUE) {
- rl_printf("Failed to ListFolders: %s\n", error.name);
+ bt_shell_printf("Failed to ListFolders: %s\n", error.name);
dbus_error_free(&error);
return;
}
dbus_message_iter_recurse(&array, &entry);
dbus_message_iter_get_basic(&entry, &obj);
- rl_printf("\t%s\n", obj);
+ bt_shell_printf("\t%s\n", obj);
dbus_message_iter_next(&array);
}
}
{
if (g_dbus_proxy_method_call(proxy, "List", list_setup, list_reply,
NULL, NULL) == FALSE) {
- rl_printf("Failed to List\n");
+ bt_shell_printf("Failed to List\n");
return;
}
- rl_printf("Attempting to List\n");
+ bt_shell_printf("Attempting to List\n");
}
static void get_size_reply(DBusMessage *message, void *user_data)
dbus_error_init(&error);
if (dbus_set_error_from_message(&error, message) == TRUE) {
- rl_printf("Failed to GetSize: %s\n", error.name);
+ bt_shell_printf("Failed to GetSize: %s\n", error.name);
dbus_error_free(&error);
return;
}
{
if (g_dbus_proxy_method_call(proxy, "GetSize", NULL, get_size_reply,
proxy, NULL) == FALSE) {
- rl_printf("Failed to GetSize\n");
+ bt_shell_printf("Failed to GetSize\n");
return;
}
- rl_printf("Attempting to GetSize\n");
+ bt_shell_printf("Attempting to GetSize\n");
}
static void pbap_ls(GDBusProxy *proxy, int argc, char *argv[])
if (g_dbus_proxy_method_call(proxy, "ListMessages", list_messages_setup,
list_messages_reply, g_strdup(argv[1]),
g_free) == FALSE) {
- rl_printf("Failed to ListMessages\n");
+ bt_shell_printf("Failed to ListMessages\n");
return;
}
- rl_printf("Attempting to ListMessages\n");
+ bt_shell_printf("Attempting to ListMessages\n");
}
static void map_ls(GDBusProxy *proxy, int argc, char *argv[])
if (g_dbus_proxy_method_call(proxy, "ListFolders", list_folders_setup,
list_folders_reply, NULL,
NULL) == FALSE) {
- rl_printf("Failed to ListFolders\n");
+ bt_shell_printf("Failed to ListFolders\n");
return;
}
- rl_printf("Attempting to ListFolders\n");
+ bt_shell_printf("Attempting to ListFolders\n");
}
static void cmd_ls(int argc, char *argv[])
return;
}
- rl_printf("Command not supported\n");
+ bt_shell_printf("Command not supported\n");
}
struct cp_args {
dbus_error_init(&error);
if (dbus_set_error_from_message(&error, message) == TRUE) {
- rl_printf("Failed to CopyFile: %s\n", error.name);
+ bt_shell_printf("Failed to CopyFile: %s\n", error.name);
dbus_error_free(&error);
return;
}
- rl_printf("CopyFile successful\n");
+ bt_shell_printf("CopyFile successful\n");
}
static void ftp_copy(GDBusProxy *proxy, int argc, char *argv[])
if (g_dbus_proxy_method_call(proxy, "CopyFile", cp_setup,
copy_file_reply, args, cp_free) == FALSE) {
- rl_printf("Failed to CopyFile\n");
+ bt_shell_printf("Failed to CopyFile\n");
return;
}
- rl_printf("Attempting to CopyFile\n");
+ bt_shell_printf("Attempting to CopyFile\n");
}
static void get_file_reply(DBusMessage *message, void *user_data)
dbus_error_init(&error);
if (dbus_set_error_from_message(&error, message) == TRUE) {
- rl_printf("Failed to GetFile: %s\n", error.name);
+ bt_shell_printf("Failed to GetFile: %s\n", error.name);
dbus_error_free(&error);
return;
}
if (g_dbus_proxy_method_call(proxy, "GetFile", get_file_setup,
get_file_reply, args, cp_free) == FALSE) {
- rl_printf("Failed to GetFile\n");
+ bt_shell_printf("Failed to GetFile\n");
return;
}
- rl_printf("Attempting to GetFile\n");
+ bt_shell_printf("Attempting to GetFile\n");
}
static void put_file_reply(DBusMessage *message, void *user_data)
dbus_error_init(&error);
if (dbus_set_error_from_message(&error, message) == TRUE) {
- rl_printf("Failed to PutFile: %s\n", error.name);
+ bt_shell_printf("Failed to PutFile: %s\n", error.name);
dbus_error_free(&error);
return;
}
struct cp_args *args;
if (rindex(argv[2], ':') != NULL) {
- rl_printf("Invalid target file argument\n");
+ bt_shell_printf("Invalid target file argument\n");
return;
}
if (g_dbus_proxy_method_call(proxy, "PutFile", cp_setup, put_file_reply,
args, cp_free) == FALSE) {
- rl_printf("Failed to PutFile\n");
+ bt_shell_printf("Failed to PutFile\n");
return;
}
- rl_printf("Attempting to PutFile\n");
+ bt_shell_printf("Attempting to PutFile\n");
}
static void ftp_cp(GDBusProxy *proxy, int argc, char *argv[])
{
- if (argc < 2) {
- rl_printf("Missing source file argument\n");
- return;
- }
-
- if (argc < 3) {
- rl_printf("Missing target file argument\n");
- return;
- }
-
if (rindex(argv[1], ':') == NULL)
return ftp_get(proxy, argc, argv);
dbus_error_init(&error);
if (dbus_set_error_from_message(&error, message) == TRUE) {
- rl_printf("Failed to PullAll: %s\n", error.name);
+ bt_shell_printf("Failed to PullAll: %s\n", error.name);
dbus_error_free(&error);
return;
}
- rl_printf("PullAll successful\n");
+ bt_shell_printf("PullAll successful\n");
}
static void pull_all_setup(DBusMessageIter *iter, void *user_data)
if (g_dbus_proxy_method_call(proxy, "PullAll", pull_all_setup,
pull_all_reply, g_strdup(argv[2]),
g_free) == FALSE) {
- rl_printf("Failed to PullAll\n");
+ bt_shell_printf("Failed to PullAll\n");
return;
}
- rl_printf("Attempting to PullAll\n");
+ bt_shell_printf("Attempting to PullAll\n");
}
static void pull_reply(DBusMessage *message, void *user_data)
dbus_error_init(&error);
if (dbus_set_error_from_message(&error, message) == TRUE) {
- rl_printf("Failed to Pull: %s\n", error.name);
+ bt_shell_printf("Failed to Pull: %s\n", error.name);
dbus_error_free(&error);
return;
}
- rl_printf("Pull successful\n");
+ bt_shell_printf("Pull successful\n");
}
static void pull_setup(DBusMessageIter *iter, void *user_data)
if (g_dbus_proxy_method_call(proxy, "Pull", pull_setup, pull_reply,
args, cp_free) == FALSE) {
- rl_printf("Failed to Pull\n");
+ bt_shell_printf("Failed to Pull\n");
return;
}
- rl_printf("Attempting to Pull\n");
+ bt_shell_printf("Attempting to Pull\n");
}
static void pbap_cp(GDBusProxy *proxy, int argc, char *argv[])
{
- if (argc < 2) {
- rl_printf("Missing source file argument\n");
- return;
- }
-
- if (argc < 3) {
- rl_printf("Missing target file argument\n");
- return;
- }
-
- if (strcmp(argv[1], "*") == 0)
+ if (strcmp(argv[1], "*") == 0 || strcmp(argv[1], "*.vcf") == 0)
return pbap_pull_all(proxy, argc, argv);
return pbap_pull(proxy, argc, argv);
dbus_error_init(&error);
if (dbus_set_error_from_message(&error, message) == TRUE) {
- rl_printf("Failed to Get: %s\n", error.name);
+ bt_shell_printf("Failed to Get: %s\n", error.name);
dbus_error_free(&error);
return;
}
{
GDBusProxy *obj;
- if (argc < 2) {
- rl_printf("Missing message argument\n");
- return;
- }
-
obj = find_message(argv[1]);
if (obj == NULL) {
- rl_printf("Invalid message argument\n");
- return;
- }
-
- if (argc < 3) {
- rl_printf("Missing target file argument\n");
+ bt_shell_printf("Invalid message argument\n");
return;
}
if (g_dbus_proxy_method_call(obj, "Get", get_setup, get_reply,
g_strdup(argv[2]), g_free) == FALSE) {
- rl_printf("Failed to Get\n");
+ bt_shell_printf("Failed to Get\n");
return;
}
- rl_printf("Attempting to Get\n");
+ bt_shell_printf("Attempting to Get\n");
}
static void cmd_cp(int argc, char *argv[])
return;
}
- rl_printf("Command not supported\n");
+ bt_shell_printf("Command not supported\n");
}
static void move_file_reply(DBusMessage *message, void *user_data)
dbus_error_init(&error);
if (dbus_set_error_from_message(&error, message) == TRUE) {
- rl_printf("Failed to MoveFile: %s\n", error.name);
+ bt_shell_printf("Failed to MoveFile: %s\n", error.name);
dbus_error_free(&error);
return;
}
- rl_printf("MoveFile successful\n");
+ bt_shell_printf("MoveFile successful\n");
}
static void cmd_mv(int argc, char *argv[])
if (!check_default_session())
return;
- if (argc < 2) {
- rl_printf("Missing source file argument\n");
- return;
- }
-
- if (argc < 3) {
- rl_printf("Missing target file argument\n");
- return;
- }
-
proxy = find_ftp(g_dbus_proxy_get_path(default_session));
if (proxy == NULL) {
- rl_printf("Command not supported\n");
+ bt_shell_printf("Command not supported\n");
return;
}
if (g_dbus_proxy_method_call(proxy, "MoveFile", cp_setup,
move_file_reply, args, cp_free) == FALSE) {
- rl_printf("Failed to MoveFile\n");
+ bt_shell_printf("Failed to MoveFile\n");
return;
}
- rl_printf("Attempting to MoveFile\n");
+ bt_shell_printf("Attempting to MoveFile\n");
}
static void delete_reply(DBusMessage *message, void *user_data)
dbus_error_init(&error);
if (dbus_set_error_from_message(&error, message) == TRUE) {
- rl_printf("Failed to Delete: %s\n", error.name);
+ bt_shell_printf("Failed to Delete: %s\n", error.name);
dbus_error_free(&error);
return;
}
- rl_printf("Delete successful\n");
+ bt_shell_printf("Delete successful\n");
}
static void delete_setup(DBusMessageIter *iter, void *user_data)
static void ftp_rm(GDBusProxy *proxy, int argc, char *argv[])
{
- if (argc < 2) {
- rl_printf("Missing file argument\n");
- return;
- }
-
if (g_dbus_proxy_method_call(proxy, "Delete", delete_setup,
delete_reply, g_strdup(argv[1]),
g_free) == FALSE) {
- rl_printf("Failed to Delete\n");
+ bt_shell_printf("Failed to Delete\n");
return;
}
- rl_printf("Attempting to Delete\n");
+ bt_shell_printf("Attempting to Delete\n");
}
static void set_delete_reply(const DBusError *error, void *user_data)
{
if (dbus_error_is_set(error))
- rl_printf("Failed to set Deleted: %s\n", error->name);
+ bt_shell_printf("Failed to set Deleted: %s\n", error->name);
else
- rl_printf("Set Deleted successful\n");
+ bt_shell_printf("Set Deleted successful\n");
}
static void map_rm(GDBusProxy *proxy, int argc, char *argv[])
GDBusProxy *msg;
dbus_bool_t value = TRUE;
- if (argc < 2) {
- rl_printf("Missing message argument\n");
- return;
- }
-
msg = find_message(argv[1]);
if (msg == NULL) {
- rl_printf("Invalid message argument\n");
+ bt_shell_printf("Invalid message argument\n");
return;
}
if (g_dbus_proxy_set_property_basic(msg, "Deleted", DBUS_TYPE_BOOLEAN,
&value, set_delete_reply,
NULL, NULL) == FALSE) {
- rl_printf("Failed to set Deleted\n");
+ bt_shell_printf("Failed to set Deleted\n");
return;
}
- rl_printf("Attempting to set Deleted\n");
+ bt_shell_printf("Attempting to set Deleted\n");
}
static void cmd_rm(int argc, char *argv[])
return;
}
- rl_printf("Command not supported\n");
+ bt_shell_printf("Command not supported\n");
}
static void create_folder_reply(DBusMessage *message, void *user_data)
dbus_error_init(&error);
if (dbus_set_error_from_message(&error, message) == TRUE) {
- rl_printf("Failed to CreateFolder: %s\n", error.name);
+ bt_shell_printf("Failed to CreateFolder: %s\n", error.name);
dbus_error_free(&error);
return;
}
- rl_printf("CreateFolder successful\n");
+ bt_shell_printf("CreateFolder successful\n");
}
static void create_folder_setup(DBusMessageIter *iter, void *user_data)
if (!check_default_session())
return;
- if (argc < 2) {
- rl_printf("Missing folder argument\n");
- return;
- }
-
proxy = find_ftp(g_dbus_proxy_get_path(default_session));
if (proxy == NULL) {
- rl_printf("Command not supported\n");
+ bt_shell_printf("Command not supported\n");
return;
}
if (g_dbus_proxy_method_call(proxy, "CreateFolder", create_folder_setup,
create_folder_reply, g_strdup(argv[1]),
g_free) == FALSE) {
- rl_printf("Failed to CreateFolder\n");
+ bt_shell_printf("Failed to CreateFolder\n");
return;
}
- rl_printf("Attempting to CreateFolder\n");
+ bt_shell_printf("Attempting to CreateFolder\n");
}
-static const struct {
- const char *cmd;
- const char *arg;
- void (*func) (int argc, char *argv[]);
- const char *desc;
-} cmd_table[] = {
+static const struct bt_shell_menu main_menu = {
+ .name = "main",
+ .entries = {
{ "connect", "<dev> [uuid]", cmd_connect, "Connect session" },
{ "disconnect", "[session]", cmd_disconnect, "Disconnect session" },
{ "list", NULL, cmd_list, "List available sessions" },
"Move source file to destination file" },
{ "rm", "<file>", cmd_rm, "Delete file" },
{ "mkdir", "<folder>", cmd_mkdir, "Create folder" },
- { "quit", NULL, cmd_quit, "Quit program" },
- { "exit", NULL, cmd_quit },
- { "help" },
- {}
+ {} },
};
-static char *cmd_generator(const char *text, int state)
-{
- static int index, len;
- const char *cmd;
-
- if (!state) {
- index = 0;
- len = strlen(text);
- }
-
- while ((cmd = cmd_table[index].cmd)) {
- index++;
-
- if (!strncmp(cmd, text, len))
- return strdup(cmd);
- }
-
- return NULL;
-}
-
-static char **cmd_completion(const char *text, int start, int end)
-{
- char **matches = NULL;
-
- if (start == 0) {
- rl_completion_display_matches_hook = NULL;
- matches = rl_completion_matches(text, cmd_generator);
- }
-
- if (!matches)
- rl_attempted_completion_over = 1;
-
- return matches;
-}
-
-static void rl_handler(char *input)
-{
- wordexp_t w;
- int argc;
- char **argv;
- int i;
-
- if (!input) {
- rl_insert_text("quit");
- rl_redisplay();
- rl_crlf();
- g_main_loop_quit(main_loop);
- return;
- }
-
- if (!strlen(input))
- goto done;
-
- add_history(input);
-
- if (wordexp(input, &w, WRDE_NOCMD))
- goto done;
-
- if (w.we_wordc == 0)
- goto free_we;
-
- argv = w.we_wordv;
- argc = w.we_wordc;
-
- for (i = 0; cmd_table[i].cmd; i++) {
- if (strcmp(argv[0], cmd_table[i].cmd))
- continue;
-
- if (cmd_table[i].func) {
- cmd_table[i].func(argc, argv);
- goto free_we;
- }
- }
-
- if (strcmp(argv[0], "help")) {
- printf("Invalid command\n");
- goto free_we;
- }
-
- printf("Available commands:\n");
-
- for (i = 0; cmd_table[i].cmd; i++) {
- if (cmd_table[i].desc)
- printf(" %s %-*s %s\n", cmd_table[i].cmd,
- (int)(25 - strlen(cmd_table[i].cmd)),
- cmd_table[i].arg ? : "",
- cmd_table[i].desc ? : "");
- }
-
-free_we:
- wordfree(&w);
-done:
- free(input);
-}
-
-static gboolean option_version = FALSE;
-
-static GOptionEntry options[] = {
- { "version", 'v', 0, G_OPTION_ARG_NONE, &option_version,
- "Show version information and exit" },
- { NULL },
-};
-
-static gboolean signal_handler(GIOChannel *channel, GIOCondition condition,
- gpointer user_data)
-{
- static unsigned int __terminated = 0;
- struct signalfd_siginfo si;
- ssize_t result;
- int fd;
-
- if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
- g_main_loop_quit(main_loop);
- return FALSE;
- }
-
- fd = g_io_channel_unix_get_fd(channel);
-
- result = read(fd, &si, sizeof(si));
- if (result != sizeof(si))
- return FALSE;
-
- switch (si.ssi_signo) {
- case SIGINT:
- rl_replace_line("", 0);
- rl_crlf();
- rl_on_new_line();
- rl_redisplay();
- break;
- case SIGTERM:
- if (__terminated == 0) {
- rl_replace_line("", 0);
- rl_crlf();
- g_main_loop_quit(main_loop);
- }
-
- __terminated = 1;
- break;
- }
-
- return TRUE;
-}
-
-static guint setup_signalfd(void)
-{
- GIOChannel *channel;
- guint source;
- sigset_t mask;
- int fd;
-
- sigemptyset(&mask);
- sigaddset(&mask, SIGINT);
- sigaddset(&mask, SIGTERM);
-
- if (sigprocmask(SIG_BLOCK, &mask, NULL) < 0) {
- perror("Failed to set signal mask");
- return 0;
- }
-
- fd = signalfd(-1, &mask, 0);
- if (fd < 0) {
- perror("Failed to create signal descriptor");
- return 0;
- }
-
- channel = g_io_channel_unix_new(fd);
-
- g_io_channel_set_close_on_unref(channel, TRUE);
- g_io_channel_set_encoding(channel, NULL, NULL);
- g_io_channel_set_buffered(channel, FALSE);
-
- source = g_io_add_watch(channel,
- G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
- signal_handler, NULL);
-
- g_io_channel_unref(channel);
-
- return source;
-}
-
-static gboolean input_handler(GIOChannel *channel, GIOCondition condition,
- gpointer user_data)
-{
- if (condition & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) {
- g_main_loop_quit(main_loop);
- return FALSE;
- }
-
- rl_callback_read_char();
- return TRUE;
-}
-
-static guint setup_standard_input(void)
-{
- GIOChannel *channel;
- guint source;
-
- channel = g_io_channel_unix_new(fileno(stdin));
-
- source = g_io_add_watch(channel,
- G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
- input_handler, NULL);
-
- g_io_channel_unref(channel);
-
- return source;
-}
-
static void client_added(GDBusProxy *proxy)
{
if (client == NULL)
data->transferred = valu64;
if (data->size == 0) {
- rl_printf("%sTransferred: %" PRIu64 " (@%" PRIu64 "KB/s)\n",
- str, valu64, speed / 1000);
+ bt_shell_printf("%sTransferred: %" PRIu64 " (@%" PRIu64
+ "KB/s)\n", str, valu64, speed / 1000);
return;
}
seconds = (data->size - data->transferred) / speed;
minutes = seconds / 60;
seconds %= 60;
- rl_printf("%sTransferred: %" PRIu64 " (@%" PRIu64 "KB/s %02u:%02u)\n",
- str, valu64, speed / 1000, minutes, seconds);
+ bt_shell_printf("%sTransferred: %" PRIu64 " (@%" PRIu64
+ "KB/s %02u:%02u)\n", str, valu64, speed / 1000,
+ minutes, seconds);
}
static void transfer_property_changed(GDBusProxy *proxy, const char *name,
data = g_new0(struct transfer_data, 1);
- if (g_dbus_proxy_get_property(proxy, "Transfered", &iter))
+ if (g_dbus_proxy_get_property(proxy, "Transferred", &iter))
dbus_message_iter_get_basic(&iter, &data->transferred);
if (g_dbus_proxy_get_property(proxy, "Size", &iter))
int main(int argc, char *argv[])
{
- GOptionContext *context;
- GError *error = NULL;
GDBusClient *client;
- guint signal, input;
-
- context = g_option_context_new(NULL);
- g_option_context_add_main_entries(context, options, NULL);
-
- if (g_option_context_parse(context, &argc, &argv, &error) == FALSE) {
- if (error != NULL) {
- g_printerr("%s\n", error->message);
- g_error_free(error);
- } else
- g_printerr("An unknown error occurred\n");
- exit(1);
- }
-
- g_option_context_free(context);
- if (option_version == TRUE) {
- printf("%s\n", VERSION);
- exit(0);
- }
+ bt_shell_init(argc, argv, NULL);
+ bt_shell_set_menu(&main_menu);
+ bt_shell_set_prompt(PROMPT_OFF);
+ bt_shell_attach(fileno(stdin));
- main_loop = g_main_loop_new(NULL, FALSE);
#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
dbus_conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, NULL);
#else
dbus_conn = g_dbus_setup_bus(DBUS_BUS_SESSION, NULL, NULL);
#endif
-
- rl_attempted_completion_function = cmd_completion;
-
- rl_erase_empty_line = 1;
- rl_callback_handler_install(NULL, rl_handler);
-
- rl_set_prompt(PROMPT_OFF);
- rl_redisplay();
-
- input = setup_standard_input();
- signal = setup_signalfd();
client = g_dbus_client_new(dbus_conn, "org.bluez.obex",
"/org/bluez/obex");
g_dbus_client_set_proxy_handlers(client, proxy_added, proxy_removed,
property_changed, NULL);
- g_main_loop_run(main_loop);
+ bt_shell_run();
g_dbus_client_unref(client);
- g_source_remove(signal);
- g_source_remove(input);
-
- rl_message("");
- rl_callback_handler_remove();
dbus_connection_unref(dbus_conn);
- g_main_loop_unref(main_loop);
return 0;
}
my %known_entities = (
'nbsp' => ' ',
+ 'aacute' => 'á',
'eacute' => 'é',
+ 'iacute' => 'í',
+ 'oacute' => 'ó',
+ 'uacute' => 'ú',
'auml' => 'ä',
+ 'uuml' => 'ü',
+ 'Uuml' => 'Ü',
);
# better to use URI::Encode if you have it
}
foreach my $entity (map { lc $_ } $name =~ /&([^;]+);/g) {
if ($entity ne 'amp') {
- print "Unable to convert &$entity;, giving up\n";
- exit 1;
+ die "\nparse_companies.pl: Unable to convert &$entity; giving up\n";
}
}
$name =~ s/&/&/ig;
$next_is_name = 1;
# next <td> should be company name
- } elsif ($next_is_name && m|\<td.*\>(.*)\</td\>|) {
+ } elsif ($next_is_name && m|\<td.*\>(.*)\<|) {
my $name = uri_decode($1);
$name =~ s/^\s+//g; # kill leading
$name =~ s/\s+$//g; # and trailing space
+ $name =~ s/"/\\"/g; # escape double quotes
my $id = hex($identifier);
if ($id != 65535) {
print "\tcase $id:\n";
switch (hdr & 0x03) {
case 0x00:
acp_seid(level, frm);
+ break;
case 0x02:
capabilities(level, frm);
break;
switch (hdr & 0x03) {
case 0x00:
acp_seid(level, frm);
+ break;
case 0x02:
hex_dump(level + 1, frm, frm->len);
frm->ptr += frm->len;
static const uint8_t smp_sc_req_1[] = { 0x01, /* Pairing Request */
0x03, /* NoInputNoOutput */
0x00, /* OOB Flag */
- 0x09, /* Bonding - no MITM, SC */
+ 0x29, /* Bonding - no MITM, SC, CT2 */
0x10, /* Max key size */
0x0d, /* Init. key dist. */
0x0d, /* Rsp. key dist. */
fclose(fp);
+ if (symlink("/etc/dbus-1/system.conf",
+ "/usr/share/dbus-1/system.conf") < 0)
+ perror("Failed to create system.conf symlink");
+
mkdir("/run/dbus", 0755);
}
tmpdir=$(mktemp -d)
trap "rm -rf $tmpdir" EXIT
+scriptdir=$(pwd)
+
mkdir $tmpdir/lib
cp lib/bluetooth.c $tmpdir/lib/bluetooth.c.orig
cp lib/bluetooth.c $tmpdir/lib/bluetooth.c
cd $tmpdir
-path=en-us/specification/assigned-numbers/company-identifiers
-# Use "iconv -c" to strip unwanted unicode characters
-# Fixups:
-# - strip <input> tags of type "checkbox" because html2text generates UTF-8 for
-# them in some distros even when using -ascii (e.g. Fedora)
-# - replace " " (non-breaking space) with whitespace manually, because
-# some versions incorrectly convert it into "\xC2\xA0"
-curl https://www.bluetooth.org/$path | iconv -c -f utf8 -t ascii | \
- sed '/<input.*type="checkbox"/d; s/ / /g' | \
- html2text -ascii -width 160 -o identifiers.txt >/dev/null
-
-# Some versions of html2text do not replace & (e.g. Fedora)
-sed -i 's/&/\&/g' identifiers.txt
+echo -e 'const char *bt_compidtostr(int compid)\n{\n\tswitch (compid) {' > new.c
-sed -n '/^const char \*bt_compidtostr(int compid)/,/^}/p' \
- lib/bluetooth.c > old.c
+path=specifications/assigned-numbers/company-identifiers
+# Use "iconv -c" to strip unwanted unicode characters
+curl --insecure https://www.bluetooth.com/$path | \
+ $scriptdir/tools/parse_companies.pl >> new.c
-echo -e 'const char *bt_compidtostr(int compid)\n{\n\tswitch (compid) {' > new.c
-cat identifiers.txt |
- perl -ne 'm/^(\d+)\s+0x[0-9a-f]+\s+(.*)/i &&
- print "\tcase $1:\n\t\treturn \"$2\";\n"' >> new.c
if ! grep -q "return \"" new.c; then
echo "ERROR: could not parse company IDs from bluetooth.org" >&2
exit 1
fi
-if [ -n "$(tr -d '[:print:]\t\n' < new.c)" ]; then
- echo -n "ERROR: invalid non-ASCII characters found while parsing" >&2
- echo -n " company IDs. Please identify offending sequence and fix" >&2
- echo " tools/update_compids.sh accordingly." >&2
- exit 1
-fi
echo -e '\tcase 65535:\n\t\treturn "internal use";' >> new.c
echo -e '\tdefault:\n\t\treturn "not assigned";\n\t}\n}' >> new.c
+sed -n '/^const char \*bt_compidtostr(int compid)/,/^}/p' \
+ lib/bluetooth.c > old.c
+
diff -Naur old.c new.c | patch -sp0 lib/bluetooth.c
diff -Naur lib/bluetooth.c.orig lib/bluetooth.c
.size = sizeof(data(args)), \
}
+#define false_pdu() \
+ { \
+ .valid = false, \
+ }
+
#define define_test(name, function, type, bt_uuid, db, \
test_step, args...) \
do { \
raw_pdu(0x01, 0x10, 0x09, 0x00, 0x0a), \
raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x01, 0x28), \
raw_pdu(0x01, 0x10, 0x01, 0x00, 0x0a), \
- raw_pdu(0x08, 0x01, 0x00, 0x04, 0x00, 0x02, 0x28), \
+ raw_pdu(0x08, 0x01, 0x00, 0x08, 0x00, 0x02, 0x28), \
raw_pdu(0x01, 0x08, 0x01, 0x00, 0x0a), \
- raw_pdu(0x08, 0x05, 0x00, 0x08, 0x00, 0x02, 0x28), \
- raw_pdu(0x01, 0x08, 0x05, 0x00, 0x0a), \
- raw_pdu(0x08, 0x01, 0x00, 0x04, 0x00, 0x03, 0x28), \
+ raw_pdu(0x08, 0x01, 0x00, 0x08, 0x00, 0x03, 0x28), \
raw_pdu(0x09, 0x07, 0x02, 0x00, 0x02, 0x03, 0x00, 0x00, \
0x2a), \
- raw_pdu(0x08, 0x03, 0x00, 0x04, 0x00, 0x03, 0x28), \
- raw_pdu(0x01, 0x08, 0x03, 0x00, 0x0a), \
- raw_pdu(0x04, 0x04, 0x00, 0x04, 0x00), \
- raw_pdu(0x05, 0x01, 0x04, 0x00, 0x01, 0x29), \
- raw_pdu(0x08, 0x05, 0x00, 0x08, 0x00, 0x03, 0x28), \
+ raw_pdu(0x08, 0x03, 0x00, 0x08, 0x00, 0x03, 0x28), \
raw_pdu(0x09, 0x07, 0x06, 0x00, 0x0a, 0x07, 0x00, 0x29, \
0x2a), \
raw_pdu(0x08, 0x07, 0x00, 0x08, 0x00, 0x03, 0x28), \
raw_pdu(0x01, 0x08, 0x07, 0x00, 0x0a), \
+ raw_pdu(0x04, 0x04, 0x00, 0x04, 0x00), \
+ raw_pdu(0x05, 0x01, 0x04, 0x00, 0x01, 0x29), \
raw_pdu(0x04, 0x08, 0x00, 0x08, 0x00), \
raw_pdu(0x05, 0x01, 0x08, 0x00, 0x01, 0x29)
raw_pdu(0x01, 0x10, 0x0b, 0x00, 0x0a), \
raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x01, 0x28), \
raw_pdu(0x01, 0x10, 0x01, 0x00, 0x0a), \
- raw_pdu(0x08, 0x01, 0x00, 0x04, 0x00, 0x02, 0x28), \
+ raw_pdu(0x08, 0x01, 0x00, 0x0a, 0x00, 0x02, 0x28), \
raw_pdu(0x01, 0x08, 0x01, 0x00, 0x0a), \
- raw_pdu(0x08, 0x05, 0x00, 0x0a, 0x00, 0x02, 0x28), \
- raw_pdu(0x01, 0x08, 0x05, 0x00, 0x0a), \
- raw_pdu(0x08, 0x01, 0x00, 0x04, 0x00, 0x03, 0x28), \
+ raw_pdu(0x08, 0x01, 0x00, 0x0a, 0x00, 0x03, 0x28), \
raw_pdu(0x09, 0x07, 0x02, 0x00, 0x02, 0x03, 0x00, 0x00, \
0x2a), \
- raw_pdu(0x08, 0x03, 0x00, 0x04, 0x00, 0x03, 0x28), \
- raw_pdu(0x01, 0x08, 0x03, 0x00, 0x0a), \
- raw_pdu(0x04, 0x04, 0x00, 0x04, 0x00), \
- raw_pdu(0x05, 0x01, 0x04, 0x00, 0x01, 0x29), \
- raw_pdu(0x08, 0x05, 0x00, 0x0a, 0x00, 0x03, 0x28), \
+ raw_pdu(0x08, 0x03, 0x00, 0x0a, 0x00, 0x03, 0x28), \
raw_pdu(0x09, 0x07, 0x07, 0x00, 0x0a, 0x08, 0x00, 0x29, \
0x2a), \
raw_pdu(0x08, 0x08, 0x00, 0x0a, 0x00, 0x03, 0x28), \
raw_pdu(0x01, 0x08, 0x08, 0x00, 0x0a), \
+ raw_pdu(0x04, 0x04, 0x00, 0x04, 0x00), \
+ raw_pdu(0x05, 0x01, 0x04, 0x00, 0x01, 0x29), \
raw_pdu(0x04, 0x09, 0x00, 0x0a, 0x00), \
raw_pdu(0x05, 0x01, 0x0a, 0x00, 0x01, 0x29)
raw_pdu(0x01, 0x10, 0x21, 0x03, 0x0a), \
raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x01, 0x28), \
raw_pdu(0x01, 0x10, 0x01, 0x00, 0x0a), \
- raw_pdu(0x08, 0x00, 0x01, 0x21, 0x01, 0x02, 0x28), \
+ raw_pdu(0x08, 0x00, 0x01, 0x20, 0x03, 0x02, 0x28), \
raw_pdu(0x01, 0x08, 0x00, 0x01, 0x0a), \
- raw_pdu(0x08, 0x00, 0x03, 0x20, 0x03, 0x02, 0x28), \
- raw_pdu(0x01, 0x08, 0x00, 0x03, 0x0a), \
- raw_pdu(0x08, 0x00, 0x01, 0x21, 0x01, 0x03, 0x28), \
+ raw_pdu(0x08, 0x00, 0x01, 0x20, 0x03, 0x03, 0x28), \
raw_pdu(0x09, 0x07, 0x10, 0x01, 0x02, 0x11, 0x01, 0x00, \
0x2a, 0x20, 0x01, 0x02, 0x21, 0x01, 0x01, 0x2a),\
- raw_pdu(0x08, 0x21, 0x01, 0x21, 0x01, 0x03, 0x28), \
- raw_pdu(0x01, 0x08, 0x21, 0x01, 0x0a), \
- raw_pdu(0x04, 0x12, 0x01, 0x1f, 0x01), \
- raw_pdu(0x01, 0x04, 0x12, 0x01, 0x0a), \
- raw_pdu(0x08, 0x00, 0x03, 0x20, 0x03, 0x03, 0x28), \
+ raw_pdu(0x08, 0x21, 0x01, 0x20, 0x03, 0x03, 0x28), \
raw_pdu(0x09, 0x07, 0x10, 0x03, 0x0a, 0x11, 0x03, 0x29, \
0x2a), \
raw_pdu(0x08, 0x11, 0x03, 0x20, 0x03, 0x03, 0x28), \
raw_pdu(0x01, 0x08, 0x11, 0x03, 0x0a), \
+ raw_pdu(0x04, 0x12, 0x01, 0x1f, 0x01), \
+ raw_pdu(0x01, 0x04, 0x12, 0x01, 0x0a), \
raw_pdu(0x04, 0x12, 0x03, 0x20, 0x03), \
raw_pdu(0x05, 0x01, 0x20, 0x03, 0x02, 0x29)
raw_pdu(0x01, 0x10, 0x11, 0x00, 0x0a)
#define INCLUDE_DISC_SMALL_DB \
- raw_pdu(0x08, 0x10, 0xf0, 0x18, 0xf0, 0x02, 0x28), \
+ raw_pdu(0x08, 0x01, 0x00, 0xff, 0xff, 0x02, 0x28), \
raw_pdu(0x09, 0x08, 0x11, 0xf0, 0x01, 0x00, 0x0f, 0x00, \
0x0a, 0x18), \
- raw_pdu(0x08, 0x12, 0xf0, 0x18, 0xf0, 0x02, 0x28), \
- raw_pdu(0x01, 0x08, 0x12, 0xf0, 0x0a), \
- raw_pdu(0x08, 0x01, 0x00, 0x10, 0x00, 0x02, 0x28), \
- raw_pdu(0x01, 0x08, 0x01, 0x00, 0x0a)
+ raw_pdu(0x08, 0x12, 0xf0, 0xff, 0xff, 0x02, 0x28), \
+ raw_pdu(0x01, 0x08, 0x12, 0xf0, 0x0a)
#define CHARACTERISTIC_DISC_SMALL_DB \
- raw_pdu(0x08, 0x10, 0xf0, 0x18, 0xf0, 0x03, 0x28), \
+ raw_pdu(0x08, 0x01, 0x00, 0xff, 0xff, 0x03, 0x28), \
+ raw_pdu(0x09, 0x07, 0x02, 0x00, 0xb2, 0x03, 0x00, 0x29, \
+ 0x2a), \
+ raw_pdu(0x08, 0x03, 0x00, 0xff, 0xff, 0x03, 0x28), \
raw_pdu(0x09, 0x07, 0x12, 0xf0, 0x02, 0x13, 0xf0, 0x00, \
0x2a), \
- raw_pdu(0x08, 0x13, 0xf0, 0x18, 0xf0, 0x03, 0x28), \
+ raw_pdu(0x08, 0x13, 0xf0, 0xff, 0xff, 0x03, 0x28), \
raw_pdu(0x09, 0x15, 0x14, 0xf0, 0x82, 0x15, 0xf0, 0xef, \
0xcd, 0xab, 0x89, 0x67, 0x45, 0x23, 0x01, 0x00, \
0x00, 0x00, 0x00, 0x09, 0xB0, 0x00, 0x00), \
- raw_pdu(0x08, 0x15, 0xf0, 0x18, 0xf0, 0x03, 0x28), \
+ raw_pdu(0x08, 0x15, 0xf0, 0xff, 0xff, 0x03, 0x28), \
raw_pdu(0x09, 0x07, 0x17, 0xf0, 0x02, 0x18, 0xf0, 0x01, \
0x2a), \
- raw_pdu(0x08, 0x18, 0xf0, 0x18, 0xf0, 0x03, 0x28), \
- raw_pdu(0x01, 0x08, 0x18, 0xf0, 0x0a), \
- raw_pdu(0x04, 0x16, 0xf0, 0x16, 0xf0), \
- raw_pdu(0x05, 0x01, 0x16, 0xf0, 0x00, 0x29), \
- raw_pdu(0x0a, 0x16, 0xf0), \
- raw_pdu(0x0b, 0x01, 0x00), \
- raw_pdu(0x08, 0x01, 0x00, 0x10, 0x00, 0x03, 0x28), \
- raw_pdu(0x09, 0x07, 0x02, 0x00, 0xb2, 0x03, 0x00, 0x29, \
- 0x2a), \
- raw_pdu(0x08, 0x03, 0x00, 0x10, 0x00, 0x03, 0x28), \
- raw_pdu(0x01, 0x08, 0x03, 0x00, 0x0a)
+ raw_pdu(0x08, 0x18, 0xf0, 0xff, 0xff, 0x03, 0x28), \
+ raw_pdu(0x01, 0x08, 0x18, 0xf0, 0x0a)
#define DESCRIPTOR_DISC_SMALL_DB \
raw_pdu(0x04, 0x04, 0x00, 0x10, 0x00), \
raw_pdu(0x05, 0x01, 0x04, 0x00, 0x02, 0x29, 0x05, 0x00, \
0x01, 0x29), \
raw_pdu(0x04, 0x06, 0x00, 0x10, 0x00), \
- raw_pdu(0x01, 0x04, 0x06, 0x00, 0x0a)
+ raw_pdu(0x01, 0x04, 0x06, 0x00, 0x0a), \
+ raw_pdu(0x04, 0x16, 0xf0, 0x16, 0xf0), \
+ raw_pdu(0x05, 0x01, 0x16, 0xf0, 0x00, 0x29), \
+ raw_pdu(0x0a, 0x16, 0xf0), \
+ raw_pdu(0x0b, 0x01, 0x00)
#define SMALL_DB_DISCOVERY_PDUS \
PRIMARY_DISC_SMALL_DB, \
if (pdu->valid && (pdu->size == 0)) {
test_debug("(no action expected)", "GATT: ");
context->pdu_offset++;
+
+ /* Quit the context if we processed the last PDU */
+ if (!context->data->pdu_list[context->pdu_offset].valid) {
+ context_quit(context);
+ return FALSE;
+ }
+
return send_pdu(context);
}
util_hexdump('>', buf, len, test_debug, "GATT: ");
+ util_hexdump('=', pdu->data, pdu->size, test_debug, "PDU: ");
+
g_assert_cmpint(len, ==, pdu->size);
g_assert(memcmp(buf, pdu->data, pdu->size) == 0);
bt_gatt_client_set_debug(context->client, print_debug,
"bt_gatt_client:", NULL);
- bt_gatt_client_set_ready_handler(context->client,
- client_ready_cb, context, NULL);
+ bt_gatt_client_ready_register(context->client, client_ready_cb,
+ context, NULL);
break;
default:
break;
raw_pdu(0x18, 0x01),
raw_pdu(0x01, 0x18, 0x25, 0x00, 0x06));
+ define_test_server("/robustness/unkown-request",
+ test_server, service_db_1, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0xbf, 0x00),
+ raw_pdu(0x01, 0xbf, 0x00, 0x00, 0x06));
+
+ define_test_server("/robustness/unkown-command",
+ test_server, service_db_1, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0xff, 0x00),
+ raw_pdu());
+
return tester_run();
}
#include <glib.h>
+#include "lib/bluetooth.h"
+#include "lib/uuid.h"
+
#include "src/shared/util.h"
#include "src/shared/tester.h"
+#include "src/shared/queue.h"
+#include "src/shared/att.h"
+#include "src/shared/gatt-db.h"
#include "attrib/gattrib.h"
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2015,2016 Felipe F. Tonello <eu@felipetonello.com>
+ * Copyright (C) 2016 ROLI Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+
+#define NUM_WRITE_TESTS 100
+
+#include "src/shared/tester.h"
+#include "profiles/midi/libmidi.h"
+
+struct ble_midi_packet {
+ const uint8_t *data;
+ size_t size;
+};
+
+#define BLE_MIDI_PACKET_INIT(_packet) \
+ { \
+ .data = (_packet), \
+ .size = sizeof(_packet), \
+ }
+
+struct midi_read_test {
+ const struct ble_midi_packet *ble_packet;
+ size_t ble_packet_size;
+ const snd_seq_event_t *event;
+ size_t event_size;
+};
+
+#define BLE_READ_TEST_INIT(_ble_packet, _event) \
+ { \
+ .ble_packet = (_ble_packet), \
+ .ble_packet_size = G_N_ELEMENTS(_ble_packet), \
+ .event = (_event), \
+ .event_size = G_N_ELEMENTS(_event), \
+ }
+
+struct midi_write_test {
+ const snd_seq_event_t *event;
+ size_t event_size;
+ const snd_seq_event_t *event_expect;
+ size_t event_expect_size;
+};
+
+#define BLE_WRITE_TEST_INIT(_event, _event_expect) \
+ { \
+ .event = (_event), \
+ .event_size = G_N_ELEMENTS(_event), \
+ .event_expect = (_event_expect), \
+ .event_expect_size = G_N_ELEMENTS(_event_expect), \
+ }
+
+#define BLE_WRITE_TEST_INIT_BASIC(_event) BLE_WRITE_TEST_INIT(_event, _event)
+
+#define NOTE_EVENT(_event, _channel, _note, _velocity) \
+ { \
+ .type = SND_SEQ_EVENT_##_event, \
+ .data = { \
+ .note = { \
+ .channel = (_channel), \
+ .note = (_note), \
+ .velocity = (_velocity), \
+ }, \
+ }, \
+ }
+
+#define CONTROL_EVENT(_event, _channel, _value, _param) \
+ { \
+ .type = SND_SEQ_EVENT_##_event, \
+ .data = { \
+ .control = { \
+ .channel = (_channel), \
+ .value = (_value), \
+ .param = (_param), \
+ }, \
+ }, \
+ }
+
+#define SYSEX_EVENT_RAW(_message, _size, _offset) \
+ { \
+ .type = SND_SEQ_EVENT_SYSEX, \
+ .data = { \
+ .ext = { \
+ .ptr = (void *)(_message) + (_offset), \
+ .len = (_size), \
+ }, \
+ }, \
+ }
+
+#define SYSEX_EVENT(_message) SYSEX_EVENT_RAW(_message, sizeof(_message), 0)
+
+/* Multiple messages in one packet */
+static const uint8_t packet1_1[] = {
+ 0xa6, 0x88, 0xe8, 0x00, 0x40, 0x88, 0xb8, 0x4a,
+ 0x3f, 0x88, 0x98, 0x3e, 0x0e
+};
+
+/* Several one message per packet */
+static const uint8_t packet1_2[] = {
+ 0xa6, 0xaa, 0xd8, 0x71
+};
+
+static const uint8_t packet1_3[] = {
+ 0xa6, 0xb7, 0xb8, 0x4a, 0x43
+};
+
+/* This message contains a running status message */
+static const uint8_t packet1_4[] = {
+ 0xa6, 0xc4, 0xe8, 0x7e, 0x3f, 0x7d, 0x3f, 0xc4,
+ 0x7c, 0x3f
+};
+
+/* This message contain a running status message misplaced */
+static const uint8_t packet1_5[] = {
+ 0xa6, 0xd9, 0x3e, 0x00, 0x88, 0x3e, 0x00
+};
+
+static const struct ble_midi_packet packet1[] = {
+ BLE_MIDI_PACKET_INIT(packet1_1),
+ BLE_MIDI_PACKET_INIT(packet1_2),
+ BLE_MIDI_PACKET_INIT(packet1_3),
+ BLE_MIDI_PACKET_INIT(packet1_4),
+ BLE_MIDI_PACKET_INIT(packet1_5),
+};
+
+static const snd_seq_event_t event1[] = {
+ CONTROL_EVENT(PITCHBEND, 8, 0, 0), /* Pitch Bend */
+ CONTROL_EVENT(CONTROLLER, 8, 63, 74), /* Control Change */
+ NOTE_EVENT(NOTEON, 8, 62, 14), /* Note On */
+ CONTROL_EVENT(CHANPRESS, 8, 113, 0), /* Channel Aftertouch */
+ CONTROL_EVENT(CONTROLLER, 8, 67, 74), /* Control Change*/
+ CONTROL_EVENT(PITCHBEND, 8, -2, 0), /* Pitch Bend */
+ CONTROL_EVENT(PITCHBEND, 8, -3, 0), /* Pitch Bend */
+ CONTROL_EVENT(PITCHBEND, 8, -4, 0), /* Pitch Bend */
+ NOTE_EVENT(NOTEOFF, 8, 62, 0), /* Note Off */
+};
+
+static const struct midi_read_test midi1 = BLE_READ_TEST_INIT(packet1, event1);
+
+/* Basic SysEx in one packet */
+static const uint8_t packet2_1[] = {
+ 0xa6, 0xda, 0xf0, 0x01, 0x02, 0x03, 0xda, 0xf7
+};
+
+/* SysEx across two packets */
+static const uint8_t packet2_2[] = {
+ 0xa6, 0xda, 0xf0, 0x01, 0x02, 0x03, 0x04, 0x05
+};
+
+static const uint8_t packet2_3[] = {
+ 0xa6, 0x06, 0x07, 0x08, 0x09, 0x0a, 0xdb, 0xf7
+};
+
+/* SysEx across multiple packets */
+static const uint8_t packet2_4[] = {
+ 0xa6, 0xda, 0xf0, 0x01, 0x02, 0x03, 0x04, 0x05
+};
+
+static const uint8_t packet2_5[] = {
+ 0xa6, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c
+};
+static const uint8_t packet2_6[] = {
+ 0xa6, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13
+};
+
+static const uint8_t packet2_7[] = {
+ 0xa6, 0x14, 0x15, 0x16, 0x17, 0x18, 0xdb, 0xf7
+};
+
+/* Two SysEx interleaved in two packets */
+static const uint8_t packet2_8[] = {
+ 0xa6, 0xda, 0xf0, 0x01, 0x02, 0x03, 0xda, 0xf7,
+ 0xda, 0xf0
+};
+
+static const uint8_t packet2_9[] = {
+ 0xa6, 0x06, 0x07, 0x08, 0x09, 0x0a, 0xdb, 0xf7
+};
+
+
+static const struct ble_midi_packet packet2[] = {
+ BLE_MIDI_PACKET_INIT(packet2_1),
+ BLE_MIDI_PACKET_INIT(packet2_2),
+ BLE_MIDI_PACKET_INIT(packet2_3),
+ BLE_MIDI_PACKET_INIT(packet2_4),
+ BLE_MIDI_PACKET_INIT(packet2_5),
+ BLE_MIDI_PACKET_INIT(packet2_6),
+ BLE_MIDI_PACKET_INIT(packet2_7),
+ BLE_MIDI_PACKET_INIT(packet2_8),
+ BLE_MIDI_PACKET_INIT(packet2_9),
+};
+
+static const uint8_t sysex2_1[] = {
+ 0xf0, 0x01, 0x02, 0x03, 0xf7
+};
+
+static const uint8_t sysex2_2[] = {
+ 0xf0, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0xf7
+};
+
+static const uint8_t sysex2_3[] = {
+ 0xf0, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0xf7
+};
+
+static const uint8_t sysex2_4[] = {
+ 0xf0, 0x01, 0x02, 0x03, 0xf7
+};
+
+static const uint8_t sysex2_5[] = {
+ 0xf0, 0x06, 0x07, 0x08, 0x09, 0x0a, 0xf7
+};
+
+static const snd_seq_event_t event2[] = {
+ SYSEX_EVENT(sysex2_1),
+ SYSEX_EVENT(sysex2_2),
+ SYSEX_EVENT(sysex2_3),
+ SYSEX_EVENT(sysex2_4),
+ SYSEX_EVENT(sysex2_5),
+};
+
+static const struct midi_read_test midi2 = BLE_READ_TEST_INIT(packet2, event2);
+
+static void compare_events(const snd_seq_event_t *ev1,
+ const snd_seq_event_t *ev2)
+{
+ g_assert_cmpint(ev1->type, ==, ev2->type);
+
+ switch (ev1->type) {
+ case SND_SEQ_EVENT_NOTEON:
+ case SND_SEQ_EVENT_NOTEOFF:
+ case SND_SEQ_EVENT_KEYPRESS:
+ g_assert_cmpint(ev1->data.note.channel,
+ ==,
+ ev2->data.note.channel);
+ g_assert_cmpint(ev1->data.note.note,
+ ==,
+ ev2->data.note.note);
+ g_assert_cmpint(ev1->data.note.velocity,
+ ==,
+ ev2->data.note.velocity);
+ break;
+ case SND_SEQ_EVENT_CONTROLLER:
+ g_assert_cmpint(ev1->data.control.param,
+ ==,
+ ev2->data.control.param);
+ break;
+ case SND_SEQ_EVENT_PITCHBEND:
+ case SND_SEQ_EVENT_CHANPRESS:
+ case SND_SEQ_EVENT_PGMCHANGE:
+ g_assert_cmpint(ev1->data.control.channel,
+ ==,
+ ev2->data.control.channel);
+ g_assert_cmpint(ev1->data.control.value,
+ ==,
+ ev2->data.control.value);
+ break;
+ case SND_SEQ_EVENT_SYSEX:
+ g_assert_cmpint(ev1->data.ext.len,
+ ==,
+ ev2->data.ext.len);
+ g_assert(memcmp(ev1->data.ext.ptr,
+ ev2->data.ext.ptr,
+ ev2->data.ext.len) == 0);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+}
+
+static void test_midi_reader(gconstpointer data)
+{
+ const struct midi_read_test *midi_test = data;
+ struct midi_read_parser midi;
+ int err;
+ size_t i; /* ble_packet counter */
+ size_t j; /* ble_packet length counter */
+ size_t k = 0; /* event counter */
+
+ err = midi_read_init(&midi);
+ g_assert_cmpint(err, ==, 0);
+
+ for (i = 0; i < midi_test->ble_packet_size; i++) {
+ const size_t length = midi_test->ble_packet[i].size;
+ j = 0;
+ midi_read_reset(&midi);
+ while (j < length) {
+ snd_seq_event_t ev;
+ const snd_seq_event_t *ev_expect = &midi_test->event[k];
+ size_t count;
+
+ g_assert_cmpint(k, <, midi_test->event_size);
+
+ snd_seq_ev_clear(&ev);
+
+ count = midi_read_raw(&midi,
+ midi_test->ble_packet[i].data + j,
+ length - j,
+ &ev);
+
+ g_assert_cmpuint(count, >, 0);
+
+ if (ev.type == SND_SEQ_EVENT_NONE)
+ goto _continue_loop;
+ else
+ k++;
+
+ compare_events(ev_expect, &ev);
+
+ _continue_loop:
+ j += count;
+ }
+ }
+
+ midi_read_free(&midi);
+
+ tester_test_passed();
+}
+
+static const snd_seq_event_t event3[] = {
+ CONTROL_EVENT(PITCHBEND, 8, 0, 0), /* Pitch Bend */
+ CONTROL_EVENT(CONTROLLER, 8, 63, 74), /* Control Change */
+ NOTE_EVENT(NOTEON, 8, 62, 14), /* Note On */
+ CONTROL_EVENT(CHANPRESS, 8, 113, 0), /* Channel Aftertouch */
+ CONTROL_EVENT(CONTROLLER, 8, 67, 74), /* Control Change*/
+ CONTROL_EVENT(PITCHBEND, 8, -2, 0), /* Pitch Bend */
+ CONTROL_EVENT(PITCHBEND, 8, -3, 0), /* Pitch Bend */
+ CONTROL_EVENT(PITCHBEND, 8, -4, 0), /* Pitch Bend */
+ NOTE_EVENT(NOTEOFF, 8, 62, 0), /* Note Off */
+};
+
+static const struct midi_write_test midi3 = BLE_WRITE_TEST_INIT_BASIC(event3);
+
+static const uint8_t sysex4_1[] = {
+ 0xf0, 0x01, 0x02, 0x03, 0xf7
+};
+
+static const uint8_t sysex4_2[] = {
+ 0xf0, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0xf7
+};
+
+static const uint8_t sysex4_3[] = {
+ 0xf0, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0xf7
+};
+
+static const uint8_t sysex4_4[] = {
+ 0xf0, 0x01, 0x02, 0x03, 0xf7
+};
+
+static const uint8_t sysex4_5[] = {
+ 0xf0, 0x06, 0x07, 0x08, 0x09, 0x0a, 0xf7
+};
+
+static const snd_seq_event_t event4[] = {
+ SYSEX_EVENT(sysex4_1),
+ SYSEX_EVENT(sysex4_2),
+ SYSEX_EVENT(sysex4_3),
+ SYSEX_EVENT(sysex4_4),
+ SYSEX_EVENT(sysex4_5),
+};
+
+static const struct midi_write_test midi4 = BLE_WRITE_TEST_INIT_BASIC(event4);
+
+/* Sysex split in multiple events (256 bytes each),
+ it's common for ALSA to split big sysexes into 256 bytes events */
+static const uint8_t sysex5_1[] = {
+ 0xf0, 0x1b, 0x46, 0x52, 0x68, 0x45, 0x19, 0x3d,
+ 0x70, 0x3c, 0x2b, 0x41, 0x09, 0x09, 0x28, 0x2d,
+ 0x66, 0x00, 0x4f, 0x06, 0x22, 0x30, 0x77, 0x0d,
+ 0x5d, 0x0e, 0x30, 0x21, 0x64, 0x5f, 0x72, 0x3c,
+ 0x31, 0x3a, 0x03, 0x37, 0x3f, 0x00, 0x66, 0x52,
+ 0x61, 0x6d, 0x03, 0x1e, 0x24, 0x2d, 0x33, 0x20,
+ 0x69, 0x17, 0x77, 0x36, 0x58, 0x53, 0x11, 0x25,
+ 0x2f, 0x51, 0x70, 0x4f, 0x00, 0x73, 0x6c, 0x3e,
+ 0x66, 0x62, 0x53, 0x20, 0x0e, 0x41, 0x0b, 0x0e,
+ 0x22, 0x27, 0x37, 0x14, 0x75, 0x31, 0x6f, 0x4b,
+ 0x3e, 0x4b, 0x55, 0x71, 0x33, 0x2d, 0x0d, 0x3e,
+ 0x58, 0x74, 0x4c, 0x44, 0x42, 0x2d, 0x47, 0x15,
+ 0x3c, 0x75, 0x5f, 0x10, 0x26, 0x54, 0x5a, 0x1e,
+ 0x07, 0x5a, 0x4a, 0x55, 0x55, 0x31, 0x40, 0x5d,
+ 0x7f, 0x25, 0x32, 0x0c, 0x74, 0x1a, 0x05, 0x22,
+ 0x66, 0x33, 0x5d, 0x38, 0x70, 0x15, 0x77, 0x35,
+ 0x52, 0x09, 0x3e, 0x63, 0x76, 0x37, 0x3c, 0x25,
+ 0x4c, 0x5c, 0x1e, 0x7d, 0x47, 0x0f, 0x2d, 0x67,
+ 0x6e, 0x68, 0x3c, 0x4e, 0x08, 0x6c, 0x16, 0x58,
+ 0x3d, 0x47, 0x19, 0x6d, 0x56, 0x12, 0x57, 0x56,
+ 0x5e, 0x2d, 0x0d, 0x0d, 0x43, 0x25, 0x75, 0x70,
+ 0x6a, 0x59, 0x3a, 0x7a, 0x41, 0x54, 0x03, 0x17,
+ 0x2d, 0x7a, 0x67, 0x06, 0x78, 0x53, 0x0f, 0x43,
+ 0x53, 0x4c, 0x02, 0x42, 0x68, 0x59, 0x0f, 0x51,
+ 0x74, 0x40, 0x1d, 0x64, 0x6f, 0x46, 0x3f, 0x77,
+ 0x71, 0x56, 0x2a, 0x24, 0x17, 0x25, 0x7f, 0x1f,
+ 0x60, 0x19, 0x1d, 0x75, 0x4e, 0x43, 0x3d, 0x0d,
+ 0x0d, 0x2e, 0x53, 0x44, 0x6c, 0x73, 0x7c, 0x6a,
+ 0x12, 0x02, 0x11, 0x38, 0x6c, 0x2b, 0x2c, 0x5d,
+ 0x4a, 0x48, 0x70, 0x7d, 0x44, 0x20, 0x41, 0x1e,
+ 0x15, 0x4c, 0x43, 0x33, 0x1b, 0x7e, 0x43, 0x2f,
+ 0x60, 0x6a, 0x61, 0x71, 0x21, 0x12, 0x32, 0x77,
+ 0x3c, 0x21, 0x7a, 0x5f, 0x58, 0x6c, 0x1f, 0x3a,
+ 0x68, 0x1c, 0x5d, 0x57, 0x1b, 0x0d, 0x77, 0x01,
+ 0x10, 0x31, 0x4a, 0x73, 0x03, 0x48, 0x18, 0x0a,
+ 0x32, 0x69, 0x38, 0x3f, 0x4d, 0x1a, 0x6e, 0x2f,
+ 0x30, 0x56, 0x4c, 0x66, 0x76, 0x16, 0x3c, 0x7a,
+ 0x31, 0x42, 0x40, 0x5d, 0x05, 0x33, 0x46, 0x53,
+ 0x5f, 0x2c, 0x4d, 0x0d, 0x39, 0x53, 0x20, 0x6e,
+ 0x61, 0x58, 0x12, 0x38, 0x25, 0x56, 0x22, 0x5b,
+ 0x27, 0x44, 0x27, 0x44, 0x59, 0x16, 0x77, 0x26,
+ 0x53, 0x35, 0x6e, 0x05, 0x70, 0x0f, 0x31, 0x30,
+ 0x23, 0x2c, 0x65, 0x16, 0x2d, 0x05, 0x3e, 0x22,
+ 0x6d, 0x22, 0x44, 0x3d, 0x18, 0x05, 0x10, 0x25,
+ 0x6b, 0x66, 0x69, 0x14, 0x63, 0x63, 0x1b, 0x04,
+ 0x41, 0x34, 0x6c, 0x09, 0x37, 0x6a, 0x63, 0x2e,
+ 0x70, 0x72, 0x44, 0x41, 0x33, 0x01, 0x05, 0x05,
+ 0x0b, 0x2a, 0x1a, 0x71, 0x55, 0x7e, 0x6e, 0x59,
+ 0x47, 0x7d, 0x2f, 0x44, 0x03, 0x52, 0x6e, 0x6b,
+ 0x4e, 0x11, 0x60, 0x1e, 0x0a, 0x71, 0x3d, 0x54,
+ 0x02, 0x1c, 0x73, 0x0b, 0x76, 0x32, 0x48, 0x66,
+ 0x36, 0x47, 0x6f, 0x5b, 0x6b, 0x3b, 0x14, 0x47,
+ 0x0c, 0x16, 0x6c, 0x27, 0x2a, 0x73, 0x17, 0x1d,
+ 0x16, 0x60, 0x63, 0x7b, 0x1d, 0x4f, 0x61, 0x5b,
+ 0x13, 0x20, 0x46, 0x0c, 0x71, 0x7d, 0x27, 0x43,
+ 0x49, 0x48, 0x7f, 0x3e, 0x4b, 0x7b, 0x27, 0x7b,
+ 0x73, 0x53, 0x57, 0x68, 0x05, 0x2a, 0x2f, 0x36,
+ 0x3b, 0x31, 0x11, 0x4e, 0x4c, 0x13, 0x2e, 0x06,
+ 0x06, 0x7c, 0x40, 0x37, 0x27, 0x0f, 0x01, 0x67,
+ 0x06, 0x09, 0x4b, 0x17, 0x0f, 0x4e, 0x51, 0x44,
+ 0x66, 0x6c, 0x70, 0x2a, 0x55, 0x62, 0x6d, 0x3b,
+ 0x16, 0x1b, 0x79, 0x08, 0x08, 0x77, 0x4a, 0x17,
+ 0x15, 0x47, 0x58, 0x5c, 0x5d, 0x3d, 0x12, 0x36,
+ 0x48, 0x5e, 0x51, 0x19, 0x6e, 0x5f, 0x64, 0x3c,
+ 0x62, 0x0b, 0x00, 0x15, 0x15, 0x2e, 0x4d, 0x5c,
+ 0x1b, 0x0a, 0x51, 0x1b, 0x13, 0x68, 0x14, 0x28,
+ 0x26, 0x69, 0x27, 0x52, 0x13, 0x1e, 0x19, 0x31,
+ 0x42, 0x0e, 0x3a, 0x29, 0x07, 0x41, 0x27, 0x40,
+ 0x4e, 0x68, 0x68, 0x78, 0x64, 0x36, 0x52, 0x7a,
+ 0x07, 0x6e, 0x46, 0x63, 0x4a, 0x6c, 0x5b, 0x4c,
+ 0x74, 0x14, 0x14, 0x76, 0x15, 0x2d, 0x79, 0x10,
+ 0x65, 0x48, 0x60, 0x6a, 0x1c, 0x65, 0x74, 0x73,
+ 0x56, 0x3c, 0x4b, 0x34, 0x20, 0x24, 0x36, 0x0d,
+ 0x3c, 0x59, 0x0f, 0x46, 0x47, 0x4a, 0x53, 0x62,
+ 0x63, 0x44, 0x22, 0x39, 0x15, 0x68, 0x60, 0x7b,
+ 0x73, 0x0f, 0x34, 0x79, 0x6a, 0x76, 0x4e, 0x0f,
+ 0x02, 0x5d, 0x09, 0x73, 0x76, 0x18, 0x48, 0x4f,
+ 0x72, 0x19, 0x71, 0x3c, 0x6e, 0x0b, 0x3b, 0x45,
+ 0x1c, 0x3e, 0x1b, 0x46, 0x74, 0x03, 0x5d, 0x0a,
+ 0x01, 0x62, 0x04, 0x2f, 0x6f, 0x03, 0x4c, 0x36,
+ 0x5f, 0x6a, 0x0c, 0x79, 0x34, 0x4f, 0x42, 0x6c,
+ 0x66, 0x21, 0x26, 0x21, 0x4a, 0x0e, 0x3e, 0x73,
+ 0x45, 0x43, 0x5e, 0x2a, 0x63, 0x32, 0x0b, 0x66,
+ 0x09, 0x46, 0x15, 0x46, 0x1c, 0x46, 0x10, 0x5b,
+ 0x09, 0x75, 0x67, 0x7f, 0x51, 0x6d, 0x12, 0x65,
+ 0x0d, 0x52, 0x06, 0x28, 0x61, 0x0f, 0x4e, 0x51,
+ 0x61, 0x75, 0x1f, 0x26, 0x31, 0x66, 0x34, 0x67,
+ 0x5d, 0x59, 0x2e, 0x18, 0x40, 0x63, 0x16, 0x12,
+ 0x49, 0x60, 0x1c, 0x62, 0x30, 0x21, 0x5c, 0x69,
+ 0x2c, 0x29, 0x1c, 0x3b, 0x3d, 0x13, 0x49, 0x4d,
+ 0x1f, 0x5f, 0x1d, 0x0a, 0x54, 0x1e, 0x52, 0x27,
+ 0x79, 0x79, 0x31, 0x03, 0x67, 0x02, 0x6a, 0x63,
+ 0x36, 0x5d, 0x38, 0x48, 0x1b, 0x4e, 0x5b, 0x63,
+ 0x7b, 0x7b, 0x4e, 0x71, 0x45, 0x37, 0x34, 0x44,
+ 0x03, 0x51, 0x31, 0x23, 0x0b, 0x18, 0x6d, 0x7f,
+ 0x76, 0x21, 0x17, 0x27, 0x45, 0x09, 0x0c, 0x2e,
+ 0x69, 0x74, 0x59, 0x2b, 0x75, 0x0c, 0x34, 0x0a,
+ 0x3a, 0x27, 0x25, 0x7b, 0x45, 0x0d, 0x59, 0x2f,
+ 0x2b, 0x57, 0x7e, 0x1f, 0x05, 0x62, 0x28, 0x79,
+ 0x7e, 0x1d, 0x58, 0x30, 0x35, 0x06, 0x67, 0x5b,
+ 0x7a, 0x00, 0x34, 0x32, 0x33, 0x2f, 0x68, 0x4b,
+ 0x76, 0x38, 0x7e, 0x58, 0x50, 0x56, 0x6d, 0x1f,
+ 0x14, 0x6f, 0x77, 0x39, 0x71, 0x35, 0x08, 0x44,
+ 0x3b, 0x09, 0x16, 0x19, 0x13, 0x1c, 0x67, 0x7d,
+ 0x7f, 0x56, 0x7e, 0x31, 0x6b, 0x67, 0x44, 0x76,
+ 0x53, 0x55, 0x6d, 0x3d, 0x13, 0x3b, 0x37, 0x1c,
+ 0x0b, 0x21, 0x58, 0x03, 0x31, 0x2d, 0x3d, 0x6c,
+ 0x01, 0x6d, 0x08, 0x1d, 0x03, 0x4d, 0x6e, 0x63,
+ 0x4c, 0x21, 0x2c, 0x57, 0x48, 0x07, 0x52, 0x2a,
+ 0x6d, 0x64, 0x0d, 0x56, 0x7e, 0x08, 0x3c, 0x1b,
+ 0x28, 0x04, 0x0f, 0x58, 0x0d, 0x6a, 0x73, 0x70,
+ 0x28, 0x0c, 0x6e, 0x1e, 0x09, 0x39, 0x46, 0x3e,
+ 0x62, 0x08, 0x72, 0x52, 0x42, 0x02, 0x78, 0x62,
+ 0x31, 0x73, 0x0d, 0x4d, 0x5c, 0x07, 0x64, 0x17,
+ 0x55, 0x29, 0x60, 0x07, 0x67, 0x59, 0x63, 0x78,
+ 0x73, 0x17, 0x42, 0x27, 0x0e, 0x77, 0x15, 0x60,
+ 0x07, 0x46, 0x53, 0x6a, 0x05, 0x38, 0x12, 0x14,
+ 0x1f, 0x1b, 0x11, 0x6a, 0x1d, 0x02, 0x3c, 0x05,
+ 0x75, 0x6d, 0x51, 0x16, 0x10, 0x6f, 0x02, 0x46,
+ 0x39, 0x2e, 0x37, 0x47, 0x7a, 0x5b, 0x39, 0x15,
+ 0x14, 0x4b, 0x77, 0x0b, 0x19, 0x24, 0x4d, 0x36,
+ 0x33, 0x4c, 0x6a, 0x53, 0x79, 0x69, 0x57, 0x17,
+ 0x10, 0x75, 0x1f, 0x72, 0x08, 0x71, 0x58, 0x14,
+ 0x46, 0x4a, 0x6f, 0x3c, 0x30, 0x34, 0x5b, 0x36,
+ 0x42, 0x13, 0x11, 0x45, 0x78, 0x5a, 0x57, 0x68,
+ 0x33, 0x4b, 0x21, 0x00, 0x06, 0x6b, 0x3d, 0x17,
+ 0x0e, 0x6a, 0x2b, 0x2a, 0x32, 0x3a, 0x2a, 0x46,
+ 0x79, 0x1f, 0x56, 0x40, 0x43, 0x36, 0x18, 0xf7,
+};
+
+static const snd_seq_event_t event5[] = {
+ /* SysEx over 4 events */
+ SYSEX_EVENT_RAW(sysex5_1, 256, 0),
+ SYSEX_EVENT_RAW(sysex5_1, 256, 256),
+ SYSEX_EVENT_RAW(sysex5_1, 256, 512),
+ SYSEX_EVENT_RAW(sysex5_1, 256, 768),
+};
+
+static const snd_seq_event_t event5_expect[] = {
+ SYSEX_EVENT(sysex5_1),
+};
+
+static const struct midi_write_test midi5 = BLE_WRITE_TEST_INIT(event5, event5_expect);
+
+static void test_midi_writer(gconstpointer data)
+{
+ const struct midi_write_test *midi_test = data;
+ struct midi_write_parser midi_out;
+ struct midi_read_parser midi_in;
+ size_t i; /* event counter */
+ size_t j; /* test counter */
+ struct midi_data {
+ size_t events_tested;
+ const struct midi_write_test *midi_test;
+ struct midi_read_parser *midi_in;
+ } midi_data;
+
+ void compare_events_cb(const struct midi_write_parser *parser, void *user_data) {
+ struct midi_data *midi_data = user_data;
+ const struct midi_write_test *midi_test = midi_data->midi_test;
+ struct midi_read_parser *midi_in = midi_data->midi_in;
+ size_t i = 0;
+
+ midi_read_reset(midi_in);
+
+ while (i < midi_write_data_size(parser)) {
+ snd_seq_event_t ev;
+ size_t count;
+
+ snd_seq_ev_clear(&ev);
+
+ count = midi_read_raw(midi_in, midi_write_data(parser) + i,
+ midi_write_data_size(parser) - i, &ev);
+
+ g_assert_cmpuint(count, >, 0);
+
+ if (ev.type != SND_SEQ_EVENT_NONE){
+ g_assert_cmpint(midi_data->events_tested,
+ <,
+ midi_test->event_expect_size);
+ compare_events(&midi_test->event_expect[midi_data->events_tested],
+ &ev);
+ midi_data->events_tested++;
+ }
+
+ i += count;
+ }
+ };
+
+ midi_read_init(&midi_in);
+
+ for (j = 0; j < NUM_WRITE_TESTS; j++) {
+
+ /* Range of test for different MTU sizes. The spec specifies
+ sizes of packet as MTU - 3 */
+ midi_write_init(&midi_out, g_random_int_range(5, 512));
+
+ midi_data.events_tested = 0;
+ midi_data.midi_test = midi_test;
+ midi_data.midi_in = &midi_in;
+
+ for (i = 0; i < midi_test->event_size; i++)
+ midi_read_ev(&midi_out, &midi_test->event[i],
+ compare_events_cb, &midi_data);
+
+ if (midi_write_has_data(&midi_out))
+ compare_events_cb(&midi_out, &midi_data);
+
+ g_assert_cmpint(midi_data.events_tested,
+ ==,
+ midi_test->event_expect_size);
+
+ midi_write_free(&midi_out);
+ }
+ midi_read_free(&midi_in);
+
+ tester_test_passed();
+}
+
+int main(int argc, char *argv[])
+{
+ tester_init(&argc, &argv);
+
+ tester_add("Raw BLE packets read",
+ &midi1, NULL, test_midi_reader, NULL);
+ tester_add("Raw BLE packets SysEx read",
+ &midi2, NULL, test_midi_reader, NULL);
+ tester_add("ALSA Seq events to Raw BLE packets",
+ &midi3, NULL, test_midi_writer, NULL);
+ tester_add("ALSA SysEx events to Raw BLE packets",
+ &midi4, NULL, test_midi_writer, NULL);
+ tester_add("Split ALSA SysEx events to raw BLE packets",
+ &midi5, NULL, test_midi_writer, NULL);
+
+ return tester_run();
+}