Merge BlueZ 5.20 into master
Change-Id: I2d69df1b233a4e0135fd5137bc792a7040897f2a
diff --git a/.gitignore b/.gitignore
index 803758b..2e08c21 100644
--- a/.gitignore
+++ b/.gitignore
@@ -86,7 +86,9 @@
unit/test-sdp
unit/test-lib
unit/test-mgmt
+unit/test-uhid
unit/test-hfp
+unit/test-crypto
tools/mgmt-tester
tools/smp-tester
tools/gap-tester
@@ -128,3 +130,7 @@
android/test-ipc
android/test-*.log
android/test-*.trs
+
+cscope.in.out
+cscope.out
+cscope.po.out
diff --git a/ChangeLog b/ChangeLog
index 1c9fd1a..25ce5ca 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,12 @@
+ver 5.20:
+ Fix issue with LED handling of PS3 controllers.
+ Add support for Android Bluetooth GATT server interface.
+ Add support for Android Bluetooth HID over GATT feature.
+ Add support for Android Bluetooth multi-profile feature.
+ Add support for Android Bluetooth aptX audio integration.
+
+ Note: aptX codec not included
+
ver 5.19:
Fix issue with OBEX Put-Delete and Create-Empty methods.
Fix issue with AVRCP browsable/searchable player properties.
diff --git a/Makefile.am b/Makefile.am
index 14e735d..178b0a0 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -80,7 +80,7 @@
lib_LTLIBRARIES += lib/libbluetooth.la
lib_libbluetooth_la_SOURCES = $(lib_headers) $(lib_sources)
-lib_libbluetooth_la_LDFLAGS = $(AM_LDFLAGS) -version-info 20:9:17
+lib_libbluetooth_la_LDFLAGS = $(AM_LDFLAGS) -version-info 20:10:17
lib_libbluetooth_la_DEPENDENCIES = $(local_headers)
endif
@@ -98,7 +98,8 @@
attrib_sources = attrib/att.h attrib/att-database.h attrib/att.c \
attrib/gatt.h attrib/gatt.c \
attrib/gattrib.h attrib/gattrib.c \
- attrib/gatt-service.h attrib/gatt-service.c
+ attrib/gatt-service.h attrib/gatt-service.c \
+ src/shared/crypto.h src/shared/crypto.c
btio_sources = btio/btio.h btio/btio.c
@@ -226,7 +227,8 @@
AM_CPPFLAGS = -I$(builddir)/lib -I$(srcdir)/gdbus
-unit_tests += unit/test-eir unit/test-uuid unit/test-textfile unit/test-crc
+unit_tests += unit/test-eir unit/test-uuid unit/test-textfile unit/test-crc \
+ unit/test-crypto
unit_test_eir_SOURCES = unit/test-eir.c src/eir.c src/uuid-helper.c
unit_test_eir_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@
@@ -240,6 +242,10 @@
unit_test_crc_SOURCES = unit/test-crc.c monitor/crc.h monitor/crc.c
unit_test_crc_LDADD = @GLIB_LIBS@
+unit_test_crypto_SOURCES = unit/test-crypto.c \
+ src/shared/crypto.h src/shared/crypto.c
+unit_test_crypto_LDADD = @GLIB_LIBS@
+
unit_tests += unit/test-ringbuf unit/test-queue
unit_test_ringbuf_SOURCES = unit/test-ringbuf.c \
@@ -261,6 +267,15 @@
src/shared/mgmt.h src/shared/mgmt.c
unit_test_mgmt_LDADD = @GLIB_LIBS@
+unit_tests += unit/test-uhid
+
+unit_test_uhid_SOURCES = unit/test-uhid.c \
+ src/shared/uhid.h src/shared/uhid.c \
+ src/shared/io.h src/shared/io-glib.c \
+ src/shared/util.h src/shared/util.c \
+ src/shared/queue.h src/shared/queue.c
+unit_test_uhid_LDADD = @GLIB_LIBS@
+
unit_tests += unit/test-sdp
unit_test_sdp_SOURCES = unit/test-sdp.c \
diff --git a/Makefile.plugins b/Makefile.plugins
index c6b92d4..dbcb491 100644
--- a/Makefile.plugins
+++ b/Makefile.plugins
@@ -59,7 +59,8 @@
builtin_sources += profiles/input/manager.c \
profiles/input/server.h profiles/input/server.c \
profiles/input/device.h profiles/input/device.c \
- profiles/input/uhid_copy.h profiles/input/hidp_defs.h
+ src/shared/uhid.h src/shared/uhid.c \
+ profiles/input/hidp_defs.h
builtin_modules += hog
builtin_sources += profiles/input/hog.c profiles/input/uhid_copy.h \
diff --git a/Makefile.tools b/Makefile.tools
index 5d610a7..966dd64 100644
--- a/Makefile.tools
+++ b/Makefile.tools
@@ -365,7 +365,9 @@
attrib/gattrib.c btio/btio.c \
attrib/gatttool.h attrib/interactive.c \
attrib/utils.c src/log.c client/display.c \
- client/display.h
+ client/display.h \
+ src/shared/crypto.h src/shared/crypto.c
+
attrib_gatttool_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@ -lreadline
tools_obex_client_tool_SOURCES = $(gobex_sources) $(btio_sources) \
diff --git a/android/Android.mk b/android/Android.mk
index 4235a7c..fc7b59d 100644
--- a/android/Android.mk
+++ b/android/Android.mk
@@ -3,12 +3,15 @@
# Retrieve BlueZ version from configure.ac file
BLUEZ_VERSION := `grep "^AC_INIT" $(LOCAL_PATH)/bluez/configure.ac | sed -e "s/.*,.\(.*\))/\1/"`
+ANDROID_VERSION := `echo $(PLATFORM_VERSION) | awk -F. '{ printf "0x%02d%02d%02d",$$1,$$2,$$3 }'`
+
# Specify pathmap for glib and sbc
pathmap_INCL += glib:external/bluetooth/glib \
sbc:external/bluetooth/sbc \
# Specify common compiler flags
BLUEZ_COMMON_CFLAGS := -DVERSION=\"$(BLUEZ_VERSION)\" \
+ -DANDROID_VERSION=$(ANDROID_VERSION) \
-DANDROID_STORAGEDIR=\"/data/misc/bluetooth\" \
# Enable warnings enabled in autotools build
@@ -32,6 +35,7 @@
LOCAL_SRC_FILES := \
bluez/android/main.c \
bluez/android/bluetooth.c \
+ bluez/android/hog.c \
bluez/android/hidhost.c \
bluez/android/socket.c \
bluez/android/ipc.c \
@@ -53,6 +57,8 @@
bluez/src/shared/hfp.c \
bluez/src/shared/gatt-db.c \
bluez/src/shared/io-glib.c \
+ bluez/src/shared/crypto.c \
+ bluez/src/shared/uhid.c \
bluez/src/sdpd-database.c \
bluez/src/sdpd-service.c \
bluez/src/sdpd-request.c \
@@ -252,9 +258,14 @@
include $(CLEAR_VARS)
-LOCAL_SRC_FILES := bluez/android/hal-audio.c
+LOCAL_SRC_FILES := \
+ bluez/src/shared/queue.c \
+ bluez/android/hal-audio.c \
+ bluez/android/hal-audio-sbc.c \
+ bluez/android/hal-audio-aptx.c \
LOCAL_C_INCLUDES = \
+ $(LOCAL_PATH)/bluez \
$(call include-path-for, system-core) \
$(call include-path-for, libhardware) \
$(call include-path-for, sbc) \
@@ -264,6 +275,7 @@
libsbc \
LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
+LOCAL_LDFLAGS := -ldl
LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
LOCAL_MODULE_TAGS := optional
@@ -581,3 +593,30 @@
LOCAL_EXPORT_C_INCLUDE_DIRS := $(include_path)
include $(BUILD_STATIC_LIBRARY)
+
+#
+# avtest
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ bluez/tools/avinfo.c \
+ bluez/lib/bluetooth.c \
+ bluez/lib/hci.c \
+
+LOCAL_C_INCLUDES := \
+ $(LOCAL_PATH)/bluez \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
+
+LOCAL_STATIC_LIBRARIES := \
+ bluetooth-headers \
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE := avinfo
+
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac
+
+include $(BUILD_EXECUTABLE)
diff --git a/android/Makefile.am b/android/Makefile.am
index e663790..51c8253 100644
--- a/android/Makefile.am
+++ b/android/Makefile.am
@@ -1,4 +1,7 @@
if ANDROID
+
+AM_CFLAGS += -DANDROID_VERSION=0xFFFFFF
+
android_plugindir = $(abs_top_srcdir)/android/.libs
noinst_PROGRAMS += android/system-emulator
@@ -31,8 +34,11 @@
src/shared/ringbuf.h src/shared/ringbuf.c \
src/shared/hfp.h src/shared/hfp.c \
src/shared/gatt-db.h src/shared/gatt-db.c \
+ src/shared/crypto.h src/shared/crypto.c \
+ src/shared/uhid.h src/shared/uhid.c \
android/bluetooth.h android/bluetooth.c \
android/hidhost.h android/hidhost.c \
+ android/hog.h android/hog.c \
android/ipc-common.h \
android/ipc.h android/ipc.c \
android/avdtp.h android/avdtp.c \
@@ -165,7 +171,10 @@
android_audio_a2dp_default_la_SOURCES = android/audio-msg.h \
android/hal-msg.h \
+ android/hal-audio.h \
android/hal-audio.c \
+ android/hal-audio-sbc.c \
+ android/hal-audio-aptx.c \
android/hardware/audio.h \
android/hardware/audio_effect.h \
android/hardware/hardware.h \
@@ -201,7 +210,8 @@
plugin_LTLIBRARIES += android/audio.a2dp.default.la
-android_audio_a2dp_default_la_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/android
+android_audio_a2dp_default_la_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/android \
+ @SBC_CFLAGS@
android_audio_a2dp_default_la_LIBADD = @SBC_LIBS@
@@ -214,6 +224,7 @@
android/init.bluetooth.rc \
android/hal-ipc-api.txt \
android/audio-ipc-api.txt \
+ android/cts.txt \
android/pics-rfcomm.txt \
android/pics-spp.txt \
android/pics-sdp.txt \
@@ -236,6 +247,7 @@
android/pics-iopt.txt \
android/pics-sm.txt \
android/pics-mps.txt \
+ android/pics-hogp.txt \
android/pixit-l2cap.txt \
android/pixit-gap.txt \
android/pixit-did.txt \
@@ -255,6 +267,7 @@
android/pixit-iopt.txt \
android/pixit-sm.txt \
android/pixit-mps.txt \
+ android/pixit-hogp.txt \
android/bite-rfcomm.txt \
android/bite-spp.txt \
android/pts-l2cap.txt \
@@ -275,4 +288,5 @@
android/pts-hdp.txt \
android/pts-mcap.txt \
android/pts-mps.txt \
- android/pts-sm.txt
+ android/pts-sm.txt \
+ android/pts-hogp.txt
diff --git a/android/README b/android/README
index 0895b71..435cf56 100644
--- a/android/README
+++ b/android/README
@@ -102,6 +102,10 @@
CONFIG_BT_BNEP_PROTO_FILTER
CONFIG_BRIDGE
CONFIG_UHID
+CONFIG_CRYPTO_CMAC
+CONFIG_CRYPTO_USER_API
+CONFIG_CRYPTO_USER_API_HASH
+CONFIG_CRYPTO_USER_API_SKCIPHER
Also BT chip driver needs to be enabled e.g:
CONFIG_BT_HCIBTUSB
@@ -218,9 +222,9 @@
---------------------------------------
core bluetooth.h complete
a2dp bt_av.h complete
-gatt bt_gatt.h partial
- bt_gatt_client.h partial
- bt_gatt_server.h initial
+gatt bt_gatt.h complete
+ bt_gatt_client.h complete
+ bt_gatt_server.h complete
handsfree bt_hf.h complete
hidhost bt_hh.h complete
health bt_hl.h initial
@@ -289,6 +293,14 @@
set_player_app_value_cb NULL JNI implementation
+HAL GATT
+--------
+
+methods:
+client->set_adv_data missing kernel support for vendor data
+client->connect is_direct parameter is ignored
+
+
Known Android issues
====================
@@ -320,3 +332,4 @@
HFP In-band Ring Tone +BSIR
AVRCP Player Settings HAL API present but not used
AVRCP Browsing No HAL API
+GATT Read multiple characteristics No HAL API
diff --git a/android/a2dp.c b/android/a2dp.c
index 452fdab..dd8bcb8 100644
--- a/android/a2dp.c
+++ b/android/a2dp.c
@@ -325,15 +325,101 @@
return 0;
}
+static int aac_check_config(void *caps, uint8_t caps_len, void *conf,
+ uint8_t conf_len)
+{
+ a2dp_aac_t *cap, *config;
+
+ if (conf_len != caps_len || conf_len != sizeof(a2dp_aac_t)) {
+ error("AAC: Invalid configuration size (%u)", conf_len);
+ return -EINVAL;
+ }
+
+ cap = caps;
+ config = conf;
+
+ if (!(cap->object_type & config->object_type)) {
+ error("AAC: Unsupported object type (%u) by endpoint",
+ config->object_type);
+ return -EINVAL;
+ }
+
+ if (!(AAC_GET_FREQUENCY(*cap) & AAC_GET_FREQUENCY(*config))) {
+ error("AAC: Unsupported frequency (%u) by endpoint",
+ AAC_GET_FREQUENCY(*config));
+ return -EINVAL;
+ }
+
+ if (!(cap->channels & config->channels)) {
+ error("AAC: Unsupported channels (%u) by endpoint",
+ config->channels);
+ return -EINVAL;
+ }
+
+ /* VBR support in SNK is mandatory but let's make sure we don't try to
+ * have VBR on remote which for some reason does not support it
+ */
+ if (!cap->vbr && config->vbr) {
+ error("AAC: Unsupported VBR (%u) by endpoint",
+ config->vbr);
+ return -EINVAL;
+ }
+
+ if (AAC_GET_BITRATE(*cap) < AAC_GET_BITRATE(*config))
+ return -ERANGE;
+
+ return 0;
+}
+
+static int aptx_check_config(void *caps, uint8_t caps_len, void *conf,
+ uint8_t conf_len)
+{
+ a2dp_aptx_t *cap, *config;
+
+ if (conf_len != caps_len || conf_len != sizeof(a2dp_aptx_t)) {
+ error("APTX: Invalid configuration size (%u)", conf_len);
+ return -EINVAL;
+ }
+
+ cap = caps;
+ config = conf;
+
+ if (!(cap->frequency & config->frequency)) {
+ error("APTX: Unsupported frequenct (%u) by endpoint",
+ config->frequency);
+ return -EINVAL;
+ }
+
+ if (!(cap->channel_mode & config->channel_mode)) {
+ error("APTX: Unsupported channel mode (%u) by endpoint",
+ config->channel_mode);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static int check_capabilities(struct a2dp_preset *preset,
struct avdtp_media_codec_capability *codec,
uint8_t codec_len)
{
+ a2dp_vendor_codec_t *vndcodec;
+
/* Codec specific */
switch (codec->media_codec_type) {
case A2DP_CODEC_SBC:
return sbc_check_config(codec->data, codec_len, preset->data,
preset->len);
+ case A2DP_CODEC_MPEG24:
+ return aac_check_config(codec->data, codec_len, preset->data,
+ preset->len);
+ case A2DP_CODEC_VENDOR:
+ vndcodec = (void *) codec->data;
+ if (btohl(vndcodec->vendor_id) == APTX_VENDOR_ID &&
+ btohs(vndcodec->codec_id) == APTX_CODEC_ID)
+ return aptx_check_config(codec->data, codec_len,
+ preset->data, preset->len);
+ return -EINVAL;
default:
return -EINVAL;
}
@@ -358,6 +444,26 @@
return p;
}
+static struct a2dp_preset *aac_select_range(void *caps, uint8_t caps_len,
+ void *conf, uint8_t conf_len)
+{
+ struct a2dp_preset *p;
+ a2dp_aac_t *cap, *config;
+ uint32_t bitrate;
+
+ cap = caps;
+ config = conf;
+
+ bitrate = MIN(AAC_GET_BITRATE(*cap), AAC_GET_BITRATE(*config));
+ AAC_SET_BITRATE(*config, bitrate);
+
+ p = g_new0(struct a2dp_preset, 1);
+ p->len = conf_len;
+ p->data = g_memdup(conf, p->len);
+
+ return p;
+}
+
static struct a2dp_preset *select_preset_range(struct a2dp_preset *preset,
struct avdtp_media_codec_capability *codec,
uint8_t codec_len)
@@ -367,6 +473,9 @@
case A2DP_CODEC_SBC:
return sbc_select_range(codec->data, codec_len, preset->data,
preset->len);
+ case A2DP_CODEC_MPEG24:
+ return aac_select_range(codec->data, codec_len, preset->data,
+ preset->len);
default:
return NULL;
}
@@ -541,14 +650,12 @@
avdtp_add_disconnect_cb(dev->session, disconnect_cb, dev);
+ /* Proceed to stream setup if initiator */
if (dev->io) {
+ int perr;
+
g_io_channel_unref(dev->io);
dev->io = NULL;
- }
-
- /* Proceed to stream setup if initiator */
- if (dev->state == HAL_A2DP_STATE_CONNECTING) {
- int perr;
perr = avdtp_discover(dev->session, discover_cb, dev);
if (perr < 0) {
@@ -739,6 +846,7 @@
}
dev = a2dp_device_new(&dst);
+ bt_a2dp_notify_state(dev, HAL_A2DP_STATE_CONNECTING);
signaling_connect_cb(chan, err, dev);
}
@@ -1227,6 +1335,14 @@
endpoint->caps = presets->data;
endpoint->presets = g_slist_copy(g_slist_nth(presets, 1));
+ if (endpoint->codec == A2DP_CODEC_VENDOR) {
+ a2dp_vendor_codec_t *vndcodec = (void *) endpoint->caps->data;
+
+ avdtp_sep_set_vendor_codec(endpoint->sep,
+ btohl(vndcodec->vendor_id),
+ btohs(vndcodec->codec_id));
+ }
+
endpoints = g_slist_append(endpoints, endpoint);
return endpoint->id;
@@ -1354,7 +1470,10 @@
DBG("");
- setup = find_setup(cmd->id);
+ if (cmd->id)
+ setup = find_setup(cmd->id);
+ else
+ setup = setups ? setups->data : NULL;
if (!setup) {
error("Unable to find stream for endpoint %u", cmd->id);
ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_OPEN_STREAM,
@@ -1373,6 +1492,7 @@
len = sizeof(struct audio_rsp_open_stream) +
sizeof(struct audio_preset) + setup->preset->len;
rsp = g_malloc0(len);
+ rsp->id = setup->endpoint->id;
rsp->mtu = omtu;
rsp->preset->len = setup->preset->len;
memcpy(rsp->preset->data, setup->preset->data, setup->preset->len);
diff --git a/android/android-tester.c b/android/android-tester.c
index f04f8ac..bf39517 100644
--- a/android/android-tester.c
+++ b/android/android-tester.c
@@ -1408,9 +1408,13 @@
static bt_bdaddr_t enable_done_bdaddr_val = { {0x00} };
static const char enable_done_bdname_val[] = "BlueZ for Android";
-static bt_uuid_t enable_done_uuids_val = {
- .uu = { 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00,
- 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb},
+static const char enable_done_uuids_val[] = {
+ /* Multi profile UUID */
+ 0x00, 0x00, 0x11, 0x3b, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00,
+ 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB,
+ /* Device identification profile UUID */
+ 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00,
+ 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB,
};
static bt_device_type_t enable_done_tod_val = BT_DEVICE_DEVTYPE_DUAL;
static bt_scan_mode_t enable_done_scanmode_val = BT_SCAN_MODE_NONE;
@@ -1771,9 +1775,13 @@
.expected_adapter_status = BT_STATUS_SUCCESS,
};
-static bt_uuid_t getprop_uuids = {
- .uu = { 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00,
- 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB },
+static const char getprop_uuids[] = {
+ /* Multi profile UUID */
+ 0x00, 0x00, 0x11, 0x3b, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00,
+ 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB,
+ /* Device identification profile UUID */
+ 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00,
+ 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB,
};
static struct priority_property getprop_uuids_props[] = {
diff --git a/android/audio-msg.h b/android/audio-msg.h
index 5981355..7b9553b 100644
--- a/android/audio-msg.h
+++ b/android/audio-msg.h
@@ -61,6 +61,7 @@
} __attribute__((packed));
struct audio_rsp_open_stream {
+ uint16_t id;
uint16_t mtu;
struct audio_preset preset[0];
} __attribute__((packed));
diff --git a/android/avctp.c b/android/avctp.c
index be3fed1..a70ed52 100644
--- a/android/avctp.c
+++ b/android/avctp.c
@@ -76,7 +76,6 @@
uint8_t transaction:4;
uint16_t pid;
} __attribute__ ((packed));
-#define AVCTP_HEADER_LENGTH 3
struct avc_header {
uint8_t code:4;
@@ -85,7 +84,6 @@
uint8_t subunit_type:5;
uint8_t opcode;
} __attribute__ ((packed));
-#define AVC_HEADER_LENGTH 3
#elif __BYTE_ORDER == __BIG_ENDIAN
@@ -96,7 +94,6 @@
uint8_t ipid:1;
uint16_t pid;
} __attribute__ ((packed));
-#define AVCTP_HEADER_LENGTH 3
struct avc_header {
uint8_t _hdr0:4;
@@ -105,7 +102,6 @@
uint8_t subunit_id:3;
uint8_t opcode;
} __attribute__ ((packed));
-#define AVC_HEADER_LENGTH 3
#else
#error "Unknown byte order"
diff --git a/android/avctp.h b/android/avctp.h
index 2b33858..f0da2b3 100644
--- a/android/avctp.h
+++ b/android/avctp.h
@@ -25,7 +25,11 @@
#define AVCTP_CONTROL_PSM 23
#define AVCTP_BROWSING_PSM 27
-#define AVC_MTU 512
+#define AVCTP_HEADER_LENGTH 3
+#define AVC_HEADER_LENGTH 3
+
+#define AVC_DATA_OFFSET AVCTP_HEADER_LENGTH + AVC_HEADER_LENGTH
+#define AVC_DATA_MTU 512
/* ctype entries */
#define AVC_CTYPE_CONTROL 0x0
diff --git a/android/avdtp.c b/android/avdtp.c
index 3d309b9..89f4860 100644
--- a/android/avdtp.c
+++ b/android/avdtp.c
@@ -39,8 +39,10 @@
#include <glib.h>
+#include "lib/bluetooth.h"
#include "src/log.h"
#include "avdtp.h"
+#include "../profiles/audio/a2dp-codecs.h"
#define AVDTP_PSM 25
@@ -317,6 +319,8 @@
struct avdtp_stream *stream;
struct seid_info info;
uint8_t codec;
+ uint32_t vndcodec_vendor;
+ uint16_t vndcodec_codec;
gboolean delay_reporting;
GSList *caps;
struct avdtp_sep_ind *ind;
@@ -1089,6 +1093,18 @@
if (codec_data->media_codec_type != lsep->codec)
continue;
+ /* FIXME: Add Vendor Specific Codec match to SEP callback */
+ if (lsep->codec == A2DP_CODEC_VENDOR) {
+ a2dp_vendor_codec_t *vndcodec =
+ (void *) codec_data->data;
+
+ if (btohl(vndcodec->vendor_id) != lsep->vndcodec_vendor)
+ continue;
+
+ if (btohs(vndcodec->codec_id) != lsep->vndcodec_codec)
+ continue;
+ }
+
if (sep->stream == NULL)
return sep;
}
@@ -3349,6 +3365,13 @@
return sep;
}
+void avdtp_sep_set_vendor_codec(struct avdtp_local_sep *sep, uint32_t vendor_id,
+ uint16_t codec_id)
+{
+ sep->vndcodec_vendor = vendor_id;
+ sep->vndcodec_codec = codec_id;
+}
+
int avdtp_unregister_sep(struct avdtp_local_sep *sep)
{
if (!sep)
diff --git a/android/avdtp.h b/android/avdtp.h
index 7fdf597..9d683c4 100644
--- a/android/avdtp.h
+++ b/android/avdtp.h
@@ -268,6 +268,8 @@
struct avdtp_sep_ind *ind,
struct avdtp_sep_cfm *cfm,
void *user_data);
+void avdtp_sep_set_vendor_codec(struct avdtp_local_sep *sep, uint32_t vendor_id,
+ uint16_t codec_id);
/* Find a matching pair of local and remote SEP ID's */
struct avdtp_remote_sep *avdtp_find_remote_sep(struct avdtp *session,
diff --git a/android/avrcp-lib.c b/android/avrcp-lib.c
index 036867e..3d318e7 100644
--- a/android/avrcp-lib.c
+++ b/android/avrcp-lib.c
@@ -96,6 +96,11 @@
uint16_t params_len, uint8_t *params, void *user_data);
};
+struct avrcp_continuing {
+ uint8_t pdu_id;
+ struct iovec pdu;
+};
+
struct avrcp {
struct avctp *conn;
struct avrcp_player *player;
@@ -103,6 +108,9 @@
const struct avrcp_control_handler *control_handlers;
void *control_data;
unsigned int control_id;
+ uint16_t control_mtu;
+
+ struct avrcp_continuing *continuing;
const struct avrcp_passthrough_handler *passthrough_handlers;
void *passthrough_data;
@@ -135,6 +143,12 @@
dst[2] = (src & 0x0000ff);
}
+static void continuing_free(struct avrcp_continuing *continuing)
+{
+ g_free(continuing->pdu.iov_base);
+ g_free(continuing);
+}
+
void avrcp_shutdown(struct avrcp *session)
{
if (session->conn) {
@@ -153,6 +167,9 @@
if (session->destroy)
session->destroy(session->destroy_data);
+ if (session->continuing)
+ continuing_free(session->continuing);
+
g_free(session->player);
g_free(session);
}
@@ -340,6 +357,14 @@
AVC_OP_VENDORDEP,
handle_vendordep_pdu,
session);
+ session->control_mtu = omtu - AVC_DATA_OFFSET;
+
+ /*
+ * 27.1.2 AV/C Command Frame
+ * An AV/C command frame contains up to 512 octets of data
+ */
+ if (session->control_mtu > AVC_DATA_MTU)
+ session->control_mtu = AVC_DATA_MTU;
avctp_set_destroy_cb(session->conn, disconnect_cb, session);
@@ -544,18 +569,22 @@
if (values[i] < AVRCP_EQUALIZER_OFF ||
values[i] > AVRCP_EQUALIZER_ON)
return false;
+ break;
case AVRCP_ATTRIBUTE_REPEAT_MODE:
if (values[i] < AVRCP_REPEAT_MODE_OFF ||
values[i] > AVRCP_REPEAT_MODE_GROUP)
return false;
+ break;
case AVRCP_ATTRIBUTE_SHUFFLE:
if (values[i] < AVRCP_SHUFFLE_OFF ||
values[i] > AVRCP_SHUFFLE_GROUP)
return false;
+ break;
case AVRCP_ATTRIBUTE_SCAN:
if (values[i] < AVRCP_SCAN_OFF ||
values[i] > AVRCP_SCAN_GROUP)
return false;
+ break;
}
}
@@ -751,6 +780,158 @@
player->user_data);
}
+static void continuing_new(struct avrcp *session, uint8_t pdu_id,
+ const struct iovec *iov, int iov_cnt,
+ size_t offset)
+{
+ struct avrcp_continuing *continuing;
+ int i;
+ size_t len = 0;
+
+ continuing = g_new0(struct avrcp_continuing, 1);
+ continuing->pdu_id = pdu_id;
+
+ for (i = 0; i < iov_cnt; i++) {
+ if (i == 0 && offset) {
+ len += iov[i].iov_len - offset;
+ continue;
+ }
+
+ len += iov[i].iov_len;
+ }
+
+ continuing->pdu.iov_base = g_malloc0(len);
+
+ DBG("len %zu", len);
+
+ for (i = 0; i < iov_cnt; i++) {
+ if (i == 0 && offset) {
+ memcpy(continuing->pdu.iov_base,
+ iov[i].iov_base + offset,
+ iov[i].iov_len - offset);
+ continuing->pdu.iov_len += iov[i].iov_len - offset;
+ continue;
+ }
+
+ memcpy(continuing->pdu.iov_base + continuing->pdu.iov_len,
+ iov[i].iov_base, iov[i].iov_len);
+ continuing->pdu.iov_len += iov[i].iov_len;
+ }
+
+ session->continuing = continuing;
+}
+
+static int avrcp_send_internal(struct avrcp *session, uint8_t transaction,
+ uint8_t code, uint8_t subunit,
+ uint8_t pdu_id, uint8_t type,
+ const struct iovec *iov, int iov_cnt)
+{
+ struct iovec pdu[iov_cnt + 1];
+ struct avrcp_header hdr;
+ int i;
+
+ /*
+ * If a receiver receives a start fragment or non-fragmented AVRCP
+ * Specific AV/C message when it already has an incomplete fragment
+ * from that sender then the receiver shall consider the first PDU
+ * aborted.
+ */
+ if (session->continuing) {
+ continuing_free(session->continuing);
+ session->continuing = NULL;
+ }
+
+ memset(&hdr, 0, sizeof(hdr));
+
+ pdu[0].iov_base = &hdr;
+ pdu[0].iov_len = sizeof(hdr);
+
+ hdr.packet_type = type;
+
+ for (i = 0; i < iov_cnt; i++) {
+ pdu[i + 1].iov_base = iov[i].iov_base;
+
+ if (pdu[0].iov_len + hdr.params_len + iov[i].iov_len <=
+ session->control_mtu) {
+ pdu[i + 1].iov_len = iov[i].iov_len;
+ hdr.params_len += iov[i].iov_len;
+ if (hdr.packet_type != AVRCP_PACKET_TYPE_SINGLE)
+ hdr.packet_type = AVRCP_PACKET_TYPE_END;
+ continue;
+ }
+
+ /*
+ * Only send what can fit and store the remaining in the
+ * continuing iovec
+ */
+ pdu[i + 1].iov_len = session->control_mtu -
+ (pdu[0].iov_len + hdr.params_len);
+ hdr.params_len += pdu[i + 1].iov_len;
+
+ continuing_new(session, pdu_id, &iov[i], iov_cnt - i,
+ pdu[i + 1].iov_len);
+
+ hdr.packet_type = hdr.packet_type != AVRCP_PACKET_TYPE_SINGLE ?
+ AVRCP_PACKET_TYPE_CONTINUING :
+ AVRCP_PACKET_TYPE_START;
+ break;
+ }
+
+ hton24(hdr.company_id, IEEEID_BTSIG);
+ hdr.pdu_id = pdu_id;
+ hdr.params_len = htons(hdr.params_len);
+
+ return avctp_send_vendor(session->conn, transaction, code, subunit,
+ pdu, iov_cnt + 1);
+}
+
+static ssize_t request_continuing(struct avrcp *session, uint8_t transaction,
+ uint16_t params_len, uint8_t *params,
+ void *user_data)
+{
+ struct iovec iov;
+ int err;
+
+ DBG("");
+
+ if (!params || params_len != 1 || !session->continuing ||
+ session->continuing->pdu_id != params[0])
+ return -EINVAL;
+
+ iov.iov_base = session->continuing->pdu.iov_base;
+ iov.iov_len = session->continuing->pdu.iov_len;
+
+ DBG("len %zu", iov.iov_len);
+
+ session->continuing->pdu.iov_base = NULL;
+
+ err = avrcp_send_internal(session, transaction, AVC_CTYPE_STABLE,
+ AVC_SUBUNIT_PANEL, params[0],
+ AVRCP_PACKET_TYPE_CONTINUING, &iov, 1);
+
+ g_free(iov.iov_base);
+
+ if (err < 0)
+ return -EINVAL;
+
+ return -EAGAIN;
+}
+
+static ssize_t abort_continuing(struct avrcp *session, uint8_t transaction,
+ uint16_t params_len, uint8_t *params,
+ void *user_data)
+{
+ DBG("");
+
+ if (!params || params_len != 1 || !session->continuing)
+ return -EINVAL;
+
+ continuing_free(session->continuing);
+ session->continuing = NULL;
+
+ return 0;
+}
+
static const struct avrcp_control_handler player_handlers[] = {
{ AVRCP_GET_CAPABILITIES,
AVC_CTYPE_STATUS, AVC_CTYPE_STABLE,
@@ -788,6 +969,12 @@
{ AVRCP_SET_ADDRESSED_PLAYER,
AVC_CTYPE_CONTROL, AVC_CTYPE_STABLE,
set_addressed },
+ { AVRCP_REQUEST_CONTINUING,
+ AVC_CTYPE_CONTROL, AVC_CTYPE_STABLE,
+ request_continuing },
+ { AVRCP_ABORT_CONTINUING,
+ AVC_CTYPE_CONTROL, AVC_CTYPE_ACCEPTED,
+ abort_continuing },
{ },
};
@@ -1049,28 +1236,8 @@
uint8_t subunit, uint8_t pdu_id,
const struct iovec *iov, int iov_cnt)
{
- struct iovec pdu[iov_cnt + 1];
- struct avrcp_header hdr;
- int i;
-
- memset(&hdr, 0, sizeof(hdr));
-
- pdu[0].iov_base = &hdr;
- pdu[0].iov_len = sizeof(hdr);
-
- for (i = 0; i < iov_cnt; i++) {
- pdu[i + 1].iov_base = iov[i].iov_base;
- pdu[i + 1].iov_len = iov[i].iov_len;
- hdr.params_len += iov[i].iov_len;
- }
-
- hton24(hdr.company_id, IEEEID_BTSIG);
- hdr.pdu_id = pdu_id;
- hdr.packet_type = AVRCP_PACKET_TYPE_SINGLE;
- hdr.params_len = htons(hdr.params_len);
-
- return avctp_send_vendor(session->conn, transaction, code, subunit,
- pdu, iov_cnt + 1);
+ return avrcp_send_internal(session, transaction, code, subunit, pdu_id,
+ AVRCP_PACKET_TYPE_SINGLE, iov, iov_cnt);
}
static int status2errno(uint8_t status)
@@ -1659,7 +1826,7 @@
*/
if (*number > AVRCP_ATTRIBUTE_LAST ||
1 + *number * 2 != pdu->params_len) {
- number = 0;
+ *number = 0;
return -EPROTO;
}
diff --git a/android/bluetooth.c b/android/bluetooth.c
index 379d8ea..fc5d529 100644
--- a/android/bluetooth.c
+++ b/android/bluetooth.c
@@ -53,6 +53,40 @@
#include "utils.h"
#include "bluetooth.h"
+/*
+ * bits in bitmask as defined in table 6.3 of Multi Profile Specification
+ * HFP AG + A2DP SRC + AVRCP TG + PAN (NAP/PANU) + PBAP PSE
+ */
+#define MPS_DEFAULT_MPSD ((1ULL << 0) | (1ULL << 2) | (1ULL << 4) | \
+ (1ULL << 6) | (1ULL << 8) | (1ULL << 10) | \
+ (1ULL << 12) | (1ULL << 26) | (1ULL << 28) | \
+ (1ULL << 30) | (1ULL << 32) | (1ULL << 34) | \
+ (1ULL << 36))
+
+/*
+ * bits in bitmask as defined in table 6.4 of Multi Profile Specification
+ * HFP AG + A2DP SRC + AVRCP TG + PAN (NAP/PANU) + PBAP PSE
+ */
+#define MPS_DEFAULT_MPMD ((1ULL << 1) | (1ULL << 3) | (1ULL << 5) | \
+ (1ULL << 6) | (1ULL << 8) | (1ULL << 10) | \
+ (1ULL << 12) | (1ULL << 15) | (1ULL << 18))
+
+/*
+ * bits in bitmask as defined in table 6.5 of Multi Profile Specification
+ * Note that in this table spec starts bit positions from 1 (bit 0 unused?)
+ */
+#define MPS_DEFAULT_DEPS ((1 << 1) | (1 << 2) | (1 << 3))
+
+/* MPSD bit dependent on HFP AG support */
+#define MPS_MPSD_HFP_AG_DEP ((1ULL << 0) | (1ULL << 2) | (1ULL << 4) | \
+ (1ULL << 6) | (1ULL << 8) | (1ULL << 10) | \
+ (1ULL << 12) | (1ULL << 14) | (1ULL << 16) | \
+ (1ULL << 18) | (1ULL << 26) | (1ULL << 28) | \
+ (1ULL << 30))
+
+/* MPMD bit dependent on HFP AG support */
+#define MPS_MPMD_HFP_AG_DEP (1ULL << 6)
+
#define DEFAULT_ADAPTER_NAME "BlueZ for Android"
#define DUT_MODE_FILE "/sys/kernel/debug/bluetooth/hci%u/dut_mode"
@@ -116,6 +150,15 @@
bool found; /* if device is found in current discovery session */
unsigned int confirm_id; /* mgtm command id if command pending */
+
+ bool valid_remote_csrk;
+ uint8_t remote_csrk[16];
+ uint32_t remote_sign_cnt;
+
+ bool valid_local_csrk;
+ uint8_t local_csrk[16];
+ uint32_t local_sign_cnt;
+ uint16_t gatt_ccc;
};
struct browse_req {
@@ -136,6 +179,7 @@
uint32_t current_settings;
uint32_t supported_settings;
+ bool le_scanning;
uint8_t cur_discovery_type;
uint8_t exp_discovery_type;
uint32_t discoverable_timeout;
@@ -603,6 +647,51 @@
/* TODO: Gatt attrib set*/
}
+void bt_store_gatt_ccc(const bdaddr_t *dst, uint16_t value)
+{
+ struct device *dev;
+ GKeyFile *key_file;
+ gsize length = 0;
+ char addr[18];
+ char *data;
+
+ dev = find_device(dst);
+ if (!dev)
+ return;
+
+ key_file = g_key_file_new();
+
+ if (!g_key_file_load_from_file(key_file, DEVICES_FILE, 0, NULL)) {
+ g_key_file_free(key_file);
+ return;
+ }
+
+ ba2str(dst, addr);
+
+ DBG("%s Gatt CCC %d", addr, value);
+
+ g_key_file_set_integer(key_file, addr, "GattCCC", value);
+
+ data = g_key_file_to_data(key_file, &length, NULL);
+ g_file_set_contents(DEVICES_FILE, data, length, NULL);
+ g_free(data);
+
+ g_key_file_free(key_file);
+
+ dev->gatt_ccc = value;
+}
+
+uint16_t bt_get_gatt_ccc(const bdaddr_t *addr)
+{
+ struct device *dev;
+
+ dev = find_device(addr);
+ if (!dev)
+ return 0;
+
+ return dev->gatt_ccc;
+}
+
static void store_link_key(const bdaddr_t *dst, const uint8_t *key,
uint8_t type, uint8_t pin_length)
{
@@ -707,6 +796,12 @@
} else if (!paired && !dev->bredr_paired) {
bonded_devices = g_slist_remove(bonded_devices, dev);
remove_device_info(dev, DEVICES_FILE);
+ dev->valid_local_csrk = false;
+ dev->valid_remote_csrk = false;
+ dev->local_sign_cnt = 0;
+ dev->remote_sign_cnt = 0;
+ memset(dev->local_csrk, 0, sizeof(dev->local_csrk));
+ memset(dev->remote_csrk, 0, sizeof(dev->remote_csrk));
cache_device(dev);
}
@@ -751,14 +846,14 @@
send_bond_state_change(&dev->bdaddr, status, new_bond);
}
-static void send_device_property(const bdaddr_t *bdaddr, uint8_t type,
+static void send_device_property(struct device *dev, uint8_t type,
uint16_t len, const void *val)
{
uint8_t buf[BASELEN_REMOTE_DEV_PROP + len];
struct hal_ev_remote_device_props *ev = (void *) buf;
ev->status = HAL_STATUS_SUCCESS;
- bdaddr2android(bdaddr, ev->bdaddr);
+ bdaddr2android(&dev->bdaddr, ev->bdaddr);
ev->num_props = 1;
ev->props[0].type = type;
ev->props[0].len = len;
@@ -779,8 +874,7 @@
ptr += sizeof(uint128_t);
}
- send_device_property(&dev->bdaddr, HAL_PROP_DEVICE_UUIDS, sizeof(buf),
- buf);
+ send_device_property(dev, HAL_PROP_DEVICE_UUIDS, sizeof(buf), buf);
}
static void set_device_uuids(struct device *dev, GSList *uuids)
@@ -970,7 +1064,7 @@
static uint8_t get_device_name(struct device *dev)
{
- send_device_property(&dev->bdaddr, HAL_PROP_DEVICE_NAME,
+ send_device_property(dev, HAL_PROP_DEVICE_NAME,
strlen(dev->name), dev->name);
return HAL_STATUS_SUCCESS;
@@ -1018,6 +1112,8 @@
{
struct hal_ev_ssp_request ev;
+ memset(&ev, 0, sizeof(ev));
+
bdaddr2android(&dev->bdaddr, ev.bdaddr);
memcpy(ev.name, dev->name, strlen(dev->name));
ev.class_of_dev = dev->class;
@@ -1201,8 +1297,11 @@
adapter.cur_discovery_type = type;
- if (ev->discovering)
+ if (ev->discovering) {
+ adapter.exp_discovery_type = adapter.le_scanning ?
+ SCAN_TYPE_LE : SCAN_TYPE_NONE;
return;
+ }
/* One shot notification about discovery stopped */
if (gatt_discovery_stopped_cb) {
@@ -1211,10 +1310,8 @@
}
type = adapter.exp_discovery_type;
- adapter.exp_discovery_type = SCAN_TYPE_NONE;
-
- if (type == SCAN_TYPE_NONE && gatt_device_found_cb)
- type = SCAN_TYPE_LE;
+ adapter.exp_discovery_type = adapter.le_scanning ? SCAN_TYPE_LE :
+ SCAN_TYPE_NONE;
if (type != SCAN_TYPE_NONE)
start_discovery(type);
@@ -1296,6 +1393,17 @@
return get_device_android_type(dev);
}
+bool bt_is_device_le(const bdaddr_t *addr)
+{
+ struct device *dev;
+
+ dev = find_device(addr);
+ if (!dev)
+ return false;
+
+ return dev->le;
+}
+
const char *bt_get_adapter_name(void)
{
return adapter.name;
@@ -1309,6 +1417,19 @@
return false;
}
+bool bt_device_set_uuids(const bdaddr_t *addr, GSList *uuids)
+{
+ struct device *dev;
+
+ dev = find_device(addr);
+ if (!dev)
+ return false;
+
+ set_device_uuids(dev, uuids);
+
+ return true;
+}
+
static bool rssi_above_threshold(int old, int new)
{
/* only 8 dBm or more */
@@ -1503,7 +1624,8 @@
discoverable = eir.flags & (EIR_LIM_DISC | EIR_GEN_DISC);
gatt_device_found_cb(bdaddr, bdaddr_type, rssi, data_len, data,
- discoverable);
+ discoverable,
+ dev->le_bonded);
}
if (!dev->bredr_paired && !dev->le_paired)
@@ -1796,6 +1918,142 @@
/* TODO browse services here? */
}
+static void store_csrk(struct device *dev)
+{
+ GKeyFile *key_file;
+ char key_str[33];
+ char addr[18];
+ int i;
+ gsize length = 0;
+ char *data;
+
+ ba2str(&dev->bdaddr, addr);
+
+ key_file = g_key_file_new();
+ if (!g_key_file_load_from_file(key_file, DEVICES_FILE, 0, NULL)) {
+ g_key_file_free(key_file);
+ return;
+ }
+
+ if (dev->valid_local_csrk) {
+ for (i = 0; i < 16; i++)
+ sprintf(key_str + (i * 2), "%2.2X",
+ dev->local_csrk[i]);
+
+ g_key_file_set_string(key_file, addr, "LocalCSRK", key_str);
+ }
+
+ if (dev->valid_remote_csrk) {
+ for (i = 0; i < 16; i++)
+ sprintf(key_str + (i * 2), "%2.2X",
+ dev->remote_csrk[i]);
+
+ g_key_file_set_string(key_file, addr, "RemoteCSRK", key_str);
+ }
+
+ data = g_key_file_to_data(key_file, &length, NULL);
+ g_file_set_contents(DEVICES_FILE, data, length, NULL);
+ g_free(data);
+
+ g_key_file_free(key_file);
+}
+
+static void new_csrk_callback(uint16_t index, uint16_t length,
+ const void *param, void *user_data)
+{
+ const struct mgmt_ev_new_csrk *ev = param;
+ struct device *dev;
+ char dst[18];
+
+ if (length < sizeof(*ev)) {
+ error("Too small csrk event (%u bytes)", length);
+ return;
+ }
+
+ ba2str(&ev->key.addr.bdaddr, dst);
+ dev = find_device(&ev->key.addr.bdaddr);
+ if (!dev)
+ return;
+
+ switch (ev->key.master) {
+ case 0x00:
+ memcpy(dev->local_csrk, ev->key.val, 16);
+ dev->local_sign_cnt = 0;
+ dev->valid_local_csrk = true;
+ break;
+ case 0x01:
+ memcpy(dev->remote_csrk, ev->key.val, 16);
+ dev->remote_sign_cnt = 0;
+ dev->valid_remote_csrk = true;
+ break;
+ default:
+ error("Unknown CSRK key type 02%02x", ev->key.master);
+ return;
+ }
+
+ update_device_state(dev, ev->key.addr.type, HAL_STATUS_SUCCESS, false,
+ true, !!ev->store_hint);
+
+ if (ev->store_hint)
+ store_csrk(dev);
+}
+
+static void store_irk(struct device *dev, const uint8_t *val)
+{
+ GKeyFile *key_file;
+ char key_str[33];
+ char addr[18];
+ int i;
+ gsize length = 0;
+ char *data;
+
+ ba2str(&dev->bdaddr, addr);
+
+ key_file = g_key_file_new();
+ if (!g_key_file_load_from_file(key_file, DEVICES_FILE, 0, NULL)) {
+ g_key_file_free(key_file);
+ return;
+ }
+
+ for (i = 0; i < 16; i++)
+ sprintf(key_str + (i * 2), "%2.2X", val[i]);
+
+ g_key_file_set_string(key_file, addr, "IdentityResolvingKey", key_str);
+
+ data = g_key_file_to_data(key_file, &length, NULL);
+ g_file_set_contents(DEVICES_FILE, data, length, NULL);
+ g_free(data);
+
+ g_key_file_free(key_file);
+}
+
+static void new_irk_callback(uint16_t index, uint16_t length,
+ const void *param, void *user_data)
+{
+ const struct mgmt_ev_new_irk *ev = param;
+ const struct mgmt_addr_info *addr = &ev->key.addr;
+ struct device *dev;
+ char dst[18], rpa[18];
+
+ if (length < sizeof(*ev)) {
+ error("To small New Irk Event (%u bytes)", length);
+ return;
+ }
+
+ ba2str(&ev->key.addr.bdaddr, dst);
+ ba2str(&ev->rpa, rpa);
+
+ DBG("new IRK for %s, RPA %s", dst, rpa);
+
+ /* TODO: handle new Identity to RPA mapping */
+ dev = find_device(&addr->bdaddr);
+ if (!dev)
+ return;
+
+ if (ev->store_hint)
+ store_irk(dev, ev->key.val);
+}
+
static void register_mgmt_handlers(void)
{
mgmt_register(mgmt_if, MGMT_EV_NEW_SETTINGS, adapter.index,
@@ -1845,6 +2103,12 @@
mgmt_register(mgmt_if, MGMT_EV_NEW_LONG_TERM_KEY, adapter.index,
new_long_term_key_event, NULL, NULL);
+
+ mgmt_register(mgmt_if, MGMT_EV_NEW_CSRK, adapter.index,
+ new_csrk_callback, NULL, NULL);
+
+ mgmt_register(mgmt_if, MGMT_EV_NEW_IRK, adapter.index, new_irk_callback,
+ NULL, NULL);
}
static void load_link_keys_complete(uint8_t status, uint16_t length,
@@ -1937,6 +2201,33 @@
g_free(cp);
}
+static void load_irks(GSList *irks)
+{
+ struct mgmt_cp_load_irks *cp;
+ struct mgmt_irk_info *irk;
+ size_t irk_count, cp_size;
+ GSList *l;
+
+ irk_count = g_slist_length(irks);
+
+ DBG("irks %zu", irk_count);
+
+ cp_size = sizeof(*cp) + (irk_count * sizeof(*irk));
+
+ cp = g_malloc0(cp_size);
+
+ cp->irk_count = htobs(irk_count);
+
+ for (l = irks, irk = cp->irks; l != NULL; l = g_slist_next(l), irk++)
+ memcpy(irk, irks->data, sizeof(*irk));
+
+ if (mgmt_send(mgmt_if, MGMT_OP_LOAD_IRKS, adapter.index, cp_size, cp,
+ NULL, NULL, NULL) == 0)
+ error("Failed to load IRKs");
+
+ g_free(cp);
+}
+
static uint8_t get_adapter_uuids(void)
{
struct hal_ev_adapter_props_changed *ev;
@@ -2254,6 +2545,40 @@
dev->le_bonded = true;
}
+ str = g_key_file_get_string(key_file, peer, "LocalCSRK", NULL);
+ if (str) {
+ int i;
+
+ dev->valid_local_csrk = true;
+ for (i = 0; i < 16; i++)
+ sscanf(str + (i * 2), "%02hhX", &dev->local_csrk[i]);
+
+ g_free(str);
+
+ dev->local_sign_cnt = g_key_file_get_integer(key_file, peer,
+ "LocalCSRKSignCounter", NULL);
+ }
+
+ str = g_key_file_get_string(key_file, peer, "RemoteCSRK", NULL);
+ if (str) {
+ int i;
+
+ dev->valid_remote_csrk = true;
+ for (i = 0; i < 16; i++)
+ sscanf(str + (i * 2), "%02hhX", &dev->remote_csrk[i]);
+
+ g_free(str);
+
+ dev->remote_sign_cnt = g_key_file_get_integer(key_file, peer,
+ "RemoteCSRKSignCounter", NULL);
+ }
+
+ str = g_key_file_get_string(key_file, peer, "GattCCC", NULL);
+ if (str) {
+ dev->gatt_ccc = atoi(str);
+ g_free(str);
+ }
+
str = g_key_file_get_string(key_file, peer, "Name", NULL);
if (str) {
g_free(dev->name);
@@ -2312,8 +2637,6 @@
str2ba(peer, &info->addr.bdaddr);
- info->addr.type = g_key_file_get_integer(key_file, peer, "Type", NULL);
-
for (i = 0; i < sizeof(info->val); i++)
sscanf(str + (i * 2), "%02hhX", &info->val[i]);
@@ -2350,7 +2673,8 @@
str2ba(peer, &info->addr.bdaddr);
- info->addr.type = g_key_file_get_integer(key_file, peer, "Type", NULL);
+ info->addr.type = g_key_file_get_integer(key_file, peer, "AddressType",
+ NULL);
for (i = 0; i < sizeof(info->val); i++)
sscanf(key + (i * 2), "%02hhX", &info->val[i]);
@@ -2374,6 +2698,33 @@
return info;
}
+static struct mgmt_irk_info *get_irk_info(GKeyFile *key_file, const char *peer)
+{
+ struct mgmt_irk_info *info = NULL;
+ unsigned int i;
+ char *str;
+
+ str = g_key_file_get_string(key_file, peer, "IdentityResolvingKey",
+ NULL);
+ if (!str || strlen(str) != 32)
+ goto failed;
+
+ info = g_new0(struct mgmt_irk_info, 1);
+
+ str2ba(peer, &info->addr.bdaddr);
+
+ info->addr.type = g_key_file_get_integer(key_file, peer, "AddressType",
+ NULL);
+
+ for (i = 0; i < sizeof(info->val); i++)
+ sscanf(str + (i * 2), "%02hhX", &info->val[i]);
+
+failed:
+ g_free(str);
+
+ return info;
+}
+
static time_t device_timestamp(const struct device *dev)
{
if (dev->bredr && dev->le) {
@@ -2431,6 +2782,7 @@
unsigned int i;
GSList *keys = NULL;
GSList *ltks = NULL;
+ GSList *irks = NULL;
key_file = g_key_file_new();
@@ -2441,10 +2793,12 @@
for (i = 0; i < len; i++) {
struct mgmt_link_key_info *key_info;
struct mgmt_ltk_info *ltk_info;
+ struct mgmt_irk_info *irk_info;
struct mgmt_ltk_info *slave_ltk_info;
struct device *dev;
key_info = get_key_info(key_file, devs[i]);
+ irk_info = get_irk_info(key_file, devs[i]);
ltk_info = get_ltk_info(key_file, devs[i], true);
slave_ltk_info = get_ltk_info(key_file, devs[i], false);
@@ -2457,6 +2811,9 @@
if (key_info)
keys = g_slist_prepend(keys, key_info);
+ if (irk_info)
+ irks = g_slist_prepend(irks, irk_info);
+
if (ltk_info)
ltks = g_slist_prepend(ltks, ltk_info);
@@ -2471,6 +2828,9 @@
load_ltks(ltks);
g_slist_free_full(ltks, g_free);
+ load_irks(irks);
+ g_slist_free_full(irks, g_free);
+
load_link_keys(keys, cb);
g_slist_free_full(keys, g_free);
@@ -2498,6 +2858,77 @@
error("Failed to set class of device");
}
+static sdp_record_t *mps_record(void)
+{
+ sdp_data_t *mpsd_features, *mpmd_features, *dependencies;
+ sdp_list_t *svclass_id, *pfseq, *root;
+ uuid_t root_uuid, svclass_uuid;
+ sdp_profile_desc_t profile;
+ sdp_record_t *record;
+ uint64_t mpsd_feat, mpmd_feat;
+ uint16_t deps;
+
+ record = sdp_record_alloc();
+ if (!record)
+ return NULL;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(NULL, &root_uuid);
+ sdp_set_browse_groups(record, root);
+
+ sdp_uuid16_create(&svclass_uuid, MPS_SVCLASS_ID);
+ svclass_id = sdp_list_append(NULL, &svclass_uuid);
+ sdp_set_service_classes(record, svclass_id);
+
+ sdp_uuid16_create(&profile.uuid, MPS_PROFILE_ID);
+ profile.version = 0x0100;
+ pfseq = sdp_list_append(NULL, &profile);
+ sdp_set_profile_descs(record, pfseq);
+
+ mpsd_feat = MPS_DEFAULT_MPSD;
+ mpmd_feat = MPS_DEFAULT_MPMD;
+
+ /* TODO should be configurable based on HFP AG support */
+ if (false) {
+ mpsd_feat &= MPS_MPSD_HFP_AG_DEP;
+ mpmd_feat &= MPS_MPMD_HFP_AG_DEP;
+ }
+
+ mpsd_features = sdp_data_alloc(SDP_UINT64, &mpsd_feat);
+ sdp_attr_add(record, SDP_ATTR_MPSD_SCENARIOS, mpsd_features);
+
+ mpmd_features = sdp_data_alloc(SDP_UINT64, &mpmd_feat);
+ sdp_attr_add(record, SDP_ATTR_MPMD_SCENARIOS, mpmd_features);
+
+ deps = MPS_DEFAULT_DEPS;
+ dependencies = sdp_data_alloc(SDP_UINT16, &deps);
+ sdp_attr_add(record, SDP_ATTR_MPS_DEPENDENCIES, dependencies);
+
+ sdp_set_info_attr(record, "Multi Profile", 0, 0);
+
+ sdp_list_free(pfseq, NULL);
+ sdp_list_free(root, NULL);
+ sdp_list_free(svclass_id, NULL);
+
+ return record;
+}
+
+static void add_mps_record(void)
+{
+ sdp_record_t *rec;
+
+ rec = mps_record();
+ if (!rec) {
+ error("Failed to allocate MPS record");
+ return;
+ }
+
+ if (bt_adapter_add_record(rec, 0) < 0) {
+ error("Failed to register MPS record");
+ sdp_record_free(rec);
+ }
+}
+
static void read_info_complete(uint8_t status, uint16_t length,
const void *param, void *user_data)
{
@@ -2558,6 +2989,7 @@
set_io_capability();
set_device_id();
+ add_mps_record();
missing_settings = adapter.current_settings ^
adapter.supported_settings;
@@ -3103,42 +3535,57 @@
return false;
}
+bool bt_le_register(bt_le_device_found cb)
+{
+ if (gatt_device_found_cb)
+ return false;
+
+ gatt_device_found_cb = cb;
+
+ return true;
+}
+
+void bt_le_unregister(void)
+{
+ gatt_device_found_cb = NULL;
+}
+
bool bt_le_discovery_stop(bt_le_discovery_stopped cb)
{
+ if (!(adapter.current_settings & MGMT_SETTING_POWERED))
+ return false;
+
+ adapter.le_scanning = false;
+
if (adapter.cur_discovery_type != SCAN_TYPE_LE) {
if (cb)
cb();
- gatt_device_found_cb = NULL;
-
return true;
}
if (!stop_discovery(SCAN_TYPE_LE))
return false;
- gatt_device_found_cb = NULL;
gatt_discovery_stopped_cb = cb;
adapter.exp_discovery_type = SCAN_TYPE_NONE;
return true;
}
-bool bt_le_discovery_start(bt_le_device_found cb)
+bool bt_le_discovery_start(void)
{
if (!(adapter.current_settings & MGMT_SETTING_POWERED))
return false;
- /* If core is discovering, don't bother */
- if (adapter.cur_discovery_type != SCAN_TYPE_NONE) {
- gatt_device_found_cb = cb;
- return true;
- }
+ adapter.le_scanning = true;
- if (start_discovery(SCAN_TYPE_LE)) {
- gatt_device_found_cb = cb;
+ /* If core is discovering, don't bother */
+ if (adapter.cur_discovery_type != SCAN_TYPE_NONE)
return true;
- }
+
+ if (start_discovery(SCAN_TYPE_LE))
+ return true;
return false;
}
@@ -3199,6 +3646,76 @@
return true;
}
+bool bt_get_csrk(const bdaddr_t *addr, enum bt_csrk_type type, uint8_t key[16],
+ uint32_t *sign_cnt)
+{
+ struct device *dev;
+ bool local = (type == LOCAL_CSRK);
+
+ dev = find_device(addr);
+ if (!dev)
+ return false;
+
+ if (local && dev->valid_local_csrk) {
+ memcpy(key, dev->local_csrk, 16);
+ *sign_cnt = dev->local_sign_cnt;
+ } else if (!local && dev->valid_remote_csrk) {
+ memcpy(key, dev->remote_csrk, 16);
+ *sign_cnt = dev->remote_sign_cnt;
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+static void store_sign_counter(struct device *dev, enum bt_csrk_type type)
+{
+ const char *sign_cnt_s;
+ uint32_t sign_cnt;
+ GKeyFile *key_file;
+ bool local = (type == LOCAL_CSRK);
+
+ gsize length = 0;
+ char addr[18];
+ char *data;
+
+ key_file = g_key_file_new();
+ if (!g_key_file_load_from_file(key_file, DEVICES_FILE, 0, NULL)) {
+ g_key_file_free(key_file);
+ return;
+ }
+
+ ba2str(&dev->bdaddr, addr);
+
+ sign_cnt_s = local ? "LocalCSRKSignCounter" : "RemoteCSRKSignCounter";
+ sign_cnt = local ? dev->local_sign_cnt : dev->remote_sign_cnt;
+
+ g_key_file_set_integer(key_file, addr, sign_cnt_s, sign_cnt);
+
+ data = g_key_file_to_data(key_file, &length, NULL);
+ g_file_set_contents(DEVICES_FILE, data, length, NULL);
+ g_free(data);
+
+ g_key_file_free(key_file);
+}
+
+void bt_update_sign_counter(const bdaddr_t *addr, enum bt_csrk_type type)
+{
+ struct device *dev;
+
+ dev = find_device(addr);
+ if (!dev)
+ return;
+
+ if (type == LOCAL_CSRK)
+ dev->local_sign_cnt++;
+ else
+ dev->remote_sign_cnt++;
+
+ store_sign_counter(dev, type);
+}
+
static uint8_t set_adapter_scan_mode(const void *buf, uint16_t len)
{
const uint8_t *mode = buf;
@@ -3636,7 +4153,7 @@
static uint8_t get_device_class(struct device *dev)
{
- send_device_property(&dev->bdaddr, HAL_PROP_DEVICE_CLASS,
+ send_device_property(dev, HAL_PROP_DEVICE_CLASS,
sizeof(dev->class), &dev->class);
return HAL_STATUS_SUCCESS;
@@ -3646,8 +4163,7 @@
{
uint8_t type = get_device_android_type(dev);
- send_device_property(&dev->bdaddr, HAL_PROP_DEVICE_TYPE,
- sizeof(type), &type);
+ send_device_property(dev, HAL_PROP_DEVICE_TYPE, sizeof(type), &type);
return HAL_STATUS_SUCCESS;
}
@@ -3666,7 +4182,7 @@
if (!dev->friendly_name)
return HAL_STATUS_FAILED;
- send_device_property(&dev->bdaddr, HAL_PROP_DEVICE_FRIENDLY_NAME,
+ send_device_property(dev, HAL_PROP_DEVICE_FRIENDLY_NAME,
strlen(dev->friendly_name), dev->friendly_name);
return HAL_STATUS_SUCCESS;
@@ -3677,7 +4193,7 @@
if (!dev->rssi)
return HAL_STATUS_FAILED;
- send_device_property(&dev->bdaddr, HAL_PROP_DEVICE_RSSI,
+ send_device_property(dev, HAL_PROP_DEVICE_RSSI,
sizeof(dev->rssi), &dev->rssi);
return HAL_STATUS_SUCCESS;
@@ -3698,7 +4214,7 @@
timestamp = device_timestamp(dev);
- send_device_property(&dev->bdaddr, HAL_PROP_DEVICE_TIMESTAMP,
+ send_device_property(dev, HAL_PROP_DEVICE_TIMESTAMP,
sizeof(timestamp), ×tamp);
return HAL_STATUS_SUCCESS;
diff --git a/android/bluetooth.h b/android/bluetooth.h
index 6a3e766..eb50fe1 100644
--- a/android/bluetooth.h
+++ b/android/bluetooth.h
@@ -21,6 +21,11 @@
*
*/
+enum bt_csrk_type {
+ LOCAL_CSRK,
+ REMOTE_CSRK,
+};
+
typedef void (*bt_bluetooth_ready)(int err, const bdaddr_t *addr);
bool bt_bluetooth_start(int index, bool mgmt_dbg, bt_bluetooth_ready cb);
@@ -37,8 +42,12 @@
typedef void (*bt_le_device_found)(const bdaddr_t *addr, uint8_t addr_type,
int rssi, uint16_t eir_len,
- const void *eir, bool discoverable);
-bool bt_le_discovery_start(bt_le_device_found cb);
+ const void *eir, bool discoverable,
+ bool bonded);
+bool bt_le_register(bt_le_device_found cb);
+void bt_le_unregister(void);
+
+bool bt_le_discovery_start(void);
typedef void (*bt_le_discovery_stopped)(void);
bool bt_le_discovery_stop(bt_le_discovery_stopped cb);
@@ -48,10 +57,22 @@
void *user_data);
uint8_t bt_get_device_android_type(const bdaddr_t *addr);
+bool bt_is_device_le(const bdaddr_t *addr);
+
const char *bt_get_adapter_name(void);
bool bt_device_is_bonded(const bdaddr_t *bdaddr);
+bool bt_device_set_uuids(const bdaddr_t *bdaddr, GSList *uuids);
typedef void (*bt_read_device_rssi_done)(uint8_t status, const bdaddr_t *addr,
int8_t rssi, void *user_data);
bool bt_read_device_rssi(const bdaddr_t *addr, bt_read_device_rssi_done cb,
void *user_data);
+
+bool bt_get_csrk(const bdaddr_t *addr, enum bt_csrk_type type,
+ uint8_t key[16], uint32_t *sign_cnt);
+
+void bt_update_sign_counter(const bdaddr_t *addr, enum bt_csrk_type type);
+
+void bt_store_gatt_ccc(const bdaddr_t *addr, uint16_t value);
+
+uint16_t bt_get_gatt_ccc(const bdaddr_t *addr);
diff --git a/android/client/if-gatt.c b/android/client/if-gatt.c
index 252e89d..4b1c549 100644
--- a/android/client/if-gatt.c
+++ b/android/client/if-gatt.c
@@ -40,10 +40,11 @@
#define MAX_READ_PARAMS_STR_LEN (MAX_SRVC_ID_STR_LEN + MAX_CHAR_ID_STR_LEN \
+ MAX_UUID_STR_LEN + MAX_HEX_VAL_STR_LEN + 80)
+/* Hex arguments must have "0x" or "0X" prefix */
#define VERIFY_INT_ARG(n, v, err) \
do { \
if (n < argc) \
- v = atoi(argv[n]); \
+ v = strtol(argv[n], NULL, 0); \
else { \
haltest_error(err); \
return;\
@@ -67,6 +68,7 @@
#define VERIFY_TRANS_ID(n, v) VERIFY_INT_ARG(n, v, "No trans_id specified\n")
#define VERIFY_STATUS(n, v) VERIFY_INT_ARG(n, v, "No status specified\n")
#define VERIFY_OFFSET(n, v) VERIFY_INT_ARG(n, v, "No offset specified\n")
+#define VERIFY_TEST_ARG(n, v) VERIFY_INT_ARG(n, v, "No test arg specified\n")
#define VERIFY_HANDLE(n, v) VERIFY_HEX_ARG(n, v, "No "#v" specified\n")
#define VERIFY_SERVICE_HANDLE(n, v) VERIFY_HANDLE(n, v)
@@ -454,7 +456,7 @@
char srvc_id_buf[MAX_SRVC_ID_STR_LEN];
char char_id_buf[MAX_CHAR_ID_STR_LEN];
- haltest_info("%s: conn_id=%d status=%d srvc_id=%s char_id=%s, char_prop=%x\n",
+ haltest_info("%s: conn_id=%d status=%d srvc_id=%s char_id=%s, char_prop=0x%x\n",
__func__, conn_id, status,
btgatt_srvc_id_t2str(srvc_id, srvc_id_buf),
btgatt_gatt_id_t2str(char_id, char_id_buf), char_prop);
@@ -644,7 +646,7 @@
snprintf(server_if_str, sizeof(server_if_str), "%d", server_if);
- haltest_info("%s: status=%d server_if=%d srvc_id=%s handle=%x\n",
+ haltest_info("%s: status=%d server_if=%d srvc_id=%s handle=0x%x\n",
__func__, status, server_if,
btgatt_srvc_id_t2str(srvc_id, buf), srvc_handle);
}
@@ -654,7 +656,7 @@
int srvc_handle,
int incl_srvc_handle)
{
- haltest_info("%s: status=%d server_if=%d srvc_handle=%x inc_srvc_handle=%x\n",
+ haltest_info("%s: status=%d server_if=%d srvc_handle=0x%x inc_srvc_handle=0x%x\n",
__func__, status, server_if,
srvc_handle, incl_srvc_handle);
}
@@ -667,7 +669,7 @@
{
char buf[MAX_SRVC_ID_STR_LEN];
- haltest_info("%s: status=%d server_if=%d uuid=%s srvc_handle=%x char_handle=%x\n",
+ haltest_info("%s: status=%d server_if=%d uuid=%s srvc_handle=0x%x char_handle=0x%x\n",
__func__, status, server_if, gatt_uuid_t2str(uuid, buf),
srvc_handle, char_handle);
}
@@ -679,7 +681,7 @@
{
char buf[MAX_SRVC_ID_STR_LEN];
- haltest_info("%s: status=%d server_if=%d uuid=%s srvc_handle=%x descr_handle=%x\n",
+ haltest_info("%s: status=%d server_if=%d uuid=%s srvc_handle=0x%x descr_handle=0x%x\n",
__func__, status, server_if, gatt_uuid_t2str(uuid, buf),
srvc_handle, descr_handle);
}
@@ -687,21 +689,21 @@
/* Callback invoked in response to start_service */
static void gatts_service_started_cb(int status, int server_if, int srvc_handle)
{
- haltest_info("%s: status=%d server_if=%d srvc_handle=%x\n",
+ haltest_info("%s: status=%d server_if=%d srvc_handle=0x%x\n",
__func__, status, server_if, srvc_handle);
}
/* Callback invoked in response to stop_service */
static void gatts_service_stopped_cb(int status, int server_if, int srvc_handle)
{
- haltest_info("%s: status=%d server_if=%d srvc_handle=%x\n",
+ haltest_info("%s: status=%d server_if=%d srvc_handle=0x%x\n",
__func__, status, server_if, srvc_handle);
}
/* Callback triggered when a service has been deleted */
static void gatts_service_deleted_cb(int status, int server_if, int srvc_handle)
{
- haltest_info("%s: status=%d server_if=%d srvc_handle=%x\n",
+ haltest_info("%s: status=%d server_if=%d srvc_handle=0x%x\n",
__func__, status, server_if, srvc_handle);
}
@@ -715,7 +717,7 @@
{
char buf[MAX_ADDR_STR_LEN];
- haltest_info("%s: conn_id=%d trans_id=%d bda=%s attr_handle=%x offset=%d is_long=%d\n",
+ haltest_info("%s: conn_id=%d trans_id=%d bda=%s attr_handle=0x%x offset=%d is_long=%d\n",
__func__, conn_id, trans_id, bt_bdaddr_t2str(bda, buf),
attr_handle, offset, is_long);
}
@@ -732,7 +734,7 @@
char buf[MAX_ADDR_STR_LEN];
char valbuf[100];
- haltest_info("%s: conn_id=%d trans_id=%d bda=%s attr_handle=%x offset=%d length=%d need_rsp=%d is_prep=%d value=%s\n",
+ haltest_info("%s: conn_id=%d trans_id=%d bda=%s attr_handle=0x%x offset=%d length=%d need_rsp=%d is_prep=%d value=%s\n",
__func__, conn_id, trans_id, bt_bdaddr_t2str(bda, buf),
attr_handle, offset, length, need_rsp, is_prep,
array2str(value, length, valbuf, sizeof(valbuf)));
@@ -755,7 +757,7 @@
*/
static void gatts_response_confirmation_cb(int status, int handle)
{
- haltest_info("%s: status=%d handle=%x\n", __func__, status, handle);
+ haltest_info("%s: status=%d handle=0x%x\n", __func__, status, handle);
}
static const btgatt_server_callbacks_t btgatt_server_callbacks = {
@@ -1405,7 +1407,7 @@
VERIFY_UUID(4, &uuid);
for (i = 5; i < argc; i++)
- *u++ = atoi(argv[i]);
+ VERIFY_TEST_ARG(i, *u++);
EXEC(if_gatt->client->test_command, command, ¶ms);
}
@@ -1750,6 +1752,51 @@
len, confirm, data);
}
+/*
+ * convert hex string to uint8_t array
+ */
+static int fill_buffer(const char *str, uint8_t *out, int out_size)
+{
+ int str_len;
+ int i, j;
+ char c;
+ uint8_t b;
+
+ str_len = strlen(str);
+
+ for (i = 0, j = 0; i < out_size && j < str_len; i++, j++) {
+ c = str[j];
+
+ if (c >= 'a' && c <= 'f')
+ c += 'A' - 'a';
+
+ if (c >= '0' && c <= '9')
+ b = c - '0';
+ else if (c >= 'A' && c <= 'F')
+ b = 10 + c - 'A';
+ else
+ return 0;
+
+ j++;
+
+ c = str[j];
+
+ if (c >= 'a' && c <= 'f')
+ c += 'A' - 'a';
+
+ if (c >= '0' && c <= '9')
+ b = b * 16 + c - '0';
+ else if (c >= 'A' && c <= 'F')
+ b = b * 16 + 10 + c - 'A';
+ else
+ return 0;
+
+ out[i] = b;
+ }
+
+ return i;
+}
+
/* send_response */
static void gatts_send_response_p(int argc, const char *argv[])
@@ -1772,15 +1819,23 @@
data.attr_value.auth_req = 0;
data.attr_value.len = 0;
- if (argc <= 7) {
- haltest_error("No data specified\n");
- return;
- }
+ if (argc > 7) {
+ const char *str;
- data.attr_value.len = strlen(argv[7]);
- scan_field(argv[7], data.attr_value.len, data.attr_value.value,
+ if (strncmp(argv[7], "0X", 2) && strncmp(argv[7], "0x", 2)) {
+ haltest_error("Value must be hex string");
+ return;
+ }
+
+ str = argv[7] + 2;
+
+ data.attr_value.len = fill_buffer(str, data.attr_value.value,
sizeof(data.attr_value.value));
-
+ if (data.attr_value.len == 0) {
+ haltest_error("Failed to parse response value");
+ return;
+ }
+ }
haltest_info("conn_id %d, trans_id %d, status %d", conn_id, trans_id,
status);
diff --git a/android/cts.txt b/android/cts.txt
new file mode 100644
index 0000000..bf351b1
--- /dev/null
+++ b/android/cts.txt
@@ -0,0 +1,32 @@
+Android Compatibility Test Suite results
+
+Tested: 10-June-2014
+Android version: 4.4.2
+
+-------------------------------------------------------------------------------
+android.bluetooth.cts.BasicAdapterTest (automated tests)
+Test Name Result Notes
+-------------------------------------------------------------------------------
+testAndroidTestCaseSetupProperly PASS
+test_checkBluetoothAddress PASS
+test_enableDisable PASS
+test_getAddress PASS
+test_getBondedDevices PASS
+test_getDefaultAdapter PASS
+test_getName PASS
+test_getRemoteDevice PASS
+test_listenUsingRfcommWithServiceRecord PASS
+-------------------------------------------------------------------------------
+
+
+-------------------------------------------------------------------------------
+com.android.cts.verifier (manual tests)
+Test Name Result Notes
+-------------------------------------------------------------------------------
+Toggle Bluetooth PASS
+Insecure Client PASS
+Insecure Server PASS Required kernel patch commit ID:
+ 6453462e99e8be13a2c8f2b3535f340b742d8192
+Secure Client PASS
+Secure Server PASS
+-------------------------------------------------------------------------------
diff --git a/android/cutils/properties.h b/android/cutils/properties.h
index 8096b19..ef40f01 100644
--- a/android/cutils/properties.h
+++ b/android/cutils/properties.h
@@ -22,6 +22,7 @@
*/
#include <unistd.h>
+#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
diff --git a/android/gatt.c b/android/gatt.c
index 8e0d72a..32853fa 100644
--- a/android/gatt.c
+++ b/android/gatt.c
@@ -57,6 +57,19 @@
#define BASE_UUID16_OFFSET 12
+#define GATT_PERM_READ 0x00000001
+#define GATT_PERM_READ_ENCRYPTED 0x00000002
+#define GATT_PERM_READ_MITM 0x00000004
+#define GATT_PERM_READ_AUTHORIZATION 0x00000008
+#define GATT_PERM_WRITE 0x00000100
+#define GATT_PERM_WRITE_ENCRYPTED 0x00000200
+#define GATT_PERM_WRITE_MITM 0x00000400
+#define GATT_PERM_WRITE_AUTHORIZATION 0x00000800
+#define GATT_PERM_WRITE_SIGNED 0x00010000
+#define GATT_PERM_WRITE_SIGNED_MITM 0x00020000
+
+#define GATT_CONN_TIMEOUT 2
+
static const uint8_t BLUETOOTH_UUID[] = {
0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80,
0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
@@ -76,11 +89,6 @@
"CONNECTED",
};
-typedef enum {
- APP_CLIENT,
- APP_SERVER,
-} gatt_app_type_t;
-
struct pending_trans_data {
unsigned int id;
uint8_t opcode;
@@ -90,13 +98,12 @@
int32_t id;
uint8_t uuid[16];
- gatt_app_type_t type;
+ gatt_type_t type;
/* Valid for client applications */
struct queue *notifications;
- /* Transaction data valid for server application */
- struct pending_trans_data trans_id;
+ gatt_conn_cb_t func;
};
struct element_id {
@@ -149,8 +156,6 @@
struct queue *services;
bool partial_srvc_search;
- bool notify_services_changed;
-
guint watch_id;
guint server_id;
@@ -163,7 +168,12 @@
struct app_connection {
struct gatt_device *device;
struct gatt_app *app;
+ struct queue *transactions;
int32_t id;
+
+ guint timeout_id;
+
+ bool wait_execute_write;
};
static struct ipc *hal_ipc = NULL;
@@ -178,8 +188,18 @@
static struct queue *listen_apps = NULL;
static struct gatt_db *gatt_db = NULL;
+static uint16_t service_changed_handle = 0;
+
static GIOChannel *listening_io = NULL;
+static struct bt_crypto *crypto = NULL;
+
+static int test_client_if = 0;
+static const uint8_t TEST_UUID[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04
+};
+
static void bt_le_discovery_stop_cb(void);
static bool is_bluetooth_uuid(const uint8_t *uuid)
@@ -364,8 +384,14 @@
static struct app_connection *find_connection_by_id(int32_t conn_id)
{
- return queue_find(app_connections, match_connection_by_id,
+ struct app_connection *conn;
+
+ conn = queue_find(app_connections, match_connection_by_id,
INT_TO_PTR(conn_id));
+ if (conn && conn->device->state == DEVICE_CONNECTED)
+ return conn;
+
+ return NULL;
}
static bool match_connection_by_device(const void *data, const void *user_data)
@@ -386,7 +412,7 @@
static struct gatt_device *find_device_by_addr(const bdaddr_t *addr)
{
- return queue_find(gatt_devices, match_device_by_bdaddr, (void *)addr);
+ return queue_find(gatt_devices, match_device_by_bdaddr, addr);
}
static struct gatt_device *find_pending_device()
@@ -553,11 +579,12 @@
device->att_io = NULL;
}
- if (device->server_id > 0)
- g_attrib_unregister(device->attrib, device->server_id);
-
if (device->attrib) {
GAttrib *attrib = device->attrib;
+
+ if (device->server_id > 0)
+ g_attrib_unregister(device->attrib, device->server_id);
+
device->attrib = NULL;
g_attrib_cancel_all(attrib);
g_attrib_unref(attrib);
@@ -591,7 +618,7 @@
* too. So remove all elements and then destroy queue.
*/
- if (app->type == APP_CLIENT)
+ if (app->type == GATT_CLIENT)
while (queue_peek_head(app->notifications)) {
struct notification_data *notification;
@@ -604,160 +631,11 @@
free(app);
}
-static int register_app(const uint8_t *uuid, gatt_app_type_t app_type)
-{
- static int32_t application_id = 1;
- struct gatt_app *app;
-
- if (queue_find(gatt_apps, match_app_by_uuid, (void *) uuid)) {
- error("gatt: app uuid is already on list");
- return 0;
- }
-
- app = new0(struct gatt_app, 1);
- if (!app) {
- error("gatt: Cannot allocate memory for registering app");
- return 0;
- }
-
- app->type = app_type;
-
- if (app->type == APP_CLIENT) {
- app->notifications = queue_new();
- if (!app->notifications) {
- error("gatt: couldn't allocate notifications queue");
- destroy_gatt_app(app);
- return 0;
- }
- }
-
- memcpy(app->uuid, uuid, sizeof(app->uuid));
-
- app->id = application_id++;
-
- if (!queue_push_head(gatt_apps, app)) {
- error("gatt: Cannot push app on the list");
- destroy_gatt_app(app);
- return 0;
- }
-
- if ((app->type == APP_SERVER) &&
- !queue_push_tail(listen_apps, INT_TO_PTR(app->id))) {
- error("gatt: Cannot push server on the list");
- destroy_gatt_app(app);
- return 0;
- }
-
- return app->id;
-}
-
-static void handle_client_register(const void *buf, uint16_t len)
-{
- const struct hal_cmd_gatt_client_register *cmd = buf;
- struct hal_ev_gatt_client_register_client ev;
-
- DBG("");
-
- memset(&ev, 0, sizeof(ev));
-
- ev.client_if = register_app(cmd->uuid, APP_CLIENT);
-
- if (ev.client_if)
- ev.status = GATT_SUCCESS;
- else
- ev.status = GATT_FAILURE;
-
- /* We should send notification with given in cmd UUID */
- memcpy(ev.app_uuid, cmd->uuid, sizeof(ev.app_uuid));
-
- ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
- HAL_EV_GATT_CLIENT_REGISTER_CLIENT, sizeof(ev), &ev);
-
- ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_REGISTER,
- HAL_STATUS_SUCCESS);
-}
-
-static void send_client_disconnection_notify(struct app_connection *connection,
- int32_t status)
-{
- struct hal_ev_gatt_client_disconnect ev;
-
- ev.client_if = connection->app->id;
- ev.conn_id = connection->id;
- ev.status = status;
-
- bdaddr2android(&connection->device->bdaddr, &ev.bda);
-
- ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
- HAL_EV_GATT_CLIENT_DISCONNECT, sizeof(ev), &ev);
-}
-
-static void send_client_connection_notify(struct app_connection *connection,
- int32_t status)
-{
- struct hal_ev_gatt_client_connect ev;
-
- ev.client_if = connection->app->id;
- ev.conn_id = connection->id;
- ev.status = status;
-
- bdaddr2android(&connection->device->bdaddr, &ev.bda);
-
- ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, HAL_EV_GATT_CLIENT_CONNECT,
- sizeof(ev), &ev);
-}
-
-static void send_server_connection_notify(struct app_connection *connection,
- bool connected)
-{
- struct hal_ev_gatt_server_connection ev;
-
- ev.server_if = connection->app->id;
- ev.conn_id = connection->id;
- ev.connected = connected;
-
- bdaddr2android(&connection->device->bdaddr, &ev.bdaddr);
-
- ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
- HAL_EV_GATT_SERVER_CONNECTION, sizeof(ev), &ev);
-}
-
-static void send_app_disconnect_notify(struct app_connection *connection,
- int32_t status)
-{
- if (connection->app->type == APP_CLIENT)
- send_client_disconnection_notify(connection, status);
- else
- send_server_connection_notify(connection, !!status);
-}
-
-static void send_app_connect_notify(struct app_connection *connection,
- int32_t status)
-{
- if (connection->app->type == APP_CLIENT)
- send_client_connection_notify(connection, status);
- else
- send_server_connection_notify(connection, !status);
-}
-
-static void disconnect_notify_by_device(void *data, void *user_data)
-{
- struct app_connection *conn = data;
- struct gatt_device *dev = user_data;
-
- if (dev != conn->device)
- return;
-
- if (dev->state == DEVICE_CONNECTED)
- send_app_disconnect_notify(conn, GATT_SUCCESS);
- else if (dev->state == DEVICE_CONNECT_INIT ||
- dev->state == DEVICE_CONNECT_READY)
- send_app_connect_notify(conn, GATT_FAILURE);
-}
-
-#define READ_INIT -3
-#define READ_PENDING -2
-#define READ_FAILED -1
+enum pend_req_state {
+ REQUEST_INIT,
+ REQUEST_PENDING,
+ REQUEST_DONE,
+};
struct pending_request {
uint16_t handle;
@@ -767,6 +645,9 @@
uint8_t *filter_value;
uint16_t filter_vlen;
+
+ enum pend_req_state state;
+ uint8_t error;
};
static void destroy_pending_request(void *data)
@@ -812,10 +693,152 @@
destroy_device(device);
}
+static struct gatt_device *create_device(const bdaddr_t *addr)
+{
+ struct gatt_device *dev;
+
+ dev = new0(struct gatt_device, 1);
+ if (!dev)
+ return NULL;
+
+ bacpy(&dev->bdaddr, addr);
+
+ dev->services = queue_new();
+ if (!dev->services) {
+ error("gatt: Failed to allocate memory for client");
+ destroy_device(dev);
+ return NULL;
+ }
+
+
+ dev->pending_requests = queue_new();
+ if (!dev->pending_requests) {
+ error("gatt: Failed to allocate memory for client");
+ destroy_device(dev);
+ return NULL;
+ }
+
+ if (!queue_push_head(gatt_devices, dev)) {
+ error("gatt: Cannot push device to queue");
+ destroy_device(dev);
+ return NULL;
+ }
+
+ return device_ref(dev);
+}
+
+static void send_client_connection_notify(struct app_connection *connection,
+ int32_t status)
+{
+ struct hal_ev_gatt_client_connect ev;
+
+ if (connection->app->func) {
+ connection->app->func(&connection->device->bdaddr,
+ status == GATT_SUCCESS ? 0 : -ENOTCONN,
+ connection->device->attrib);
+ return;
+ }
+
+ ev.client_if = connection->app->id;
+ ev.conn_id = connection->id;
+ ev.status = status;
+
+ bdaddr2android(&connection->device->bdaddr, &ev.bda);
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, HAL_EV_GATT_CLIENT_CONNECT,
+ sizeof(ev), &ev);
+}
+
+static void send_server_connection_notify(struct app_connection *connection,
+ bool connected)
+{
+ struct hal_ev_gatt_server_connection ev;
+
+ if (connection->app->func) {
+ connection->app->func(&connection->device->bdaddr,
+ connected ? 0 : -ENOTCONN,
+ connection->device->attrib);
+ return;
+ }
+
+ ev.server_if = connection->app->id;
+ ev.conn_id = connection->id;
+ ev.connected = connected;
+
+ bdaddr2android(&connection->device->bdaddr, &ev.bdaddr);
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_EV_GATT_SERVER_CONNECTION, sizeof(ev), &ev);
+}
+
+static void send_client_disconnection_notify(struct app_connection *connection,
+ int32_t status)
+{
+ struct hal_ev_gatt_client_disconnect ev;
+
+ if (connection->app->func) {
+ connection->app->func(&connection->device->bdaddr, -ENOTCONN,
+ connection->device->attrib);
+ return;
+ }
+
+ ev.client_if = connection->app->id;
+ ev.conn_id = connection->id;
+ ev.status = status;
+
+ bdaddr2android(&connection->device->bdaddr, &ev.bda);
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_EV_GATT_CLIENT_DISCONNECT, sizeof(ev), &ev);
+
+}
+
+static void send_app_disconnect_notify(struct app_connection *connection,
+ int32_t status)
+{
+ if (!connection->app)
+ return;
+
+ if (connection->app->type == GATT_CLIENT)
+ send_client_disconnection_notify(connection, status);
+ else
+ send_server_connection_notify(connection, !!status);
+}
+
+static void send_app_connect_notify(struct app_connection *connection,
+ int32_t status)
+{
+ if (!connection->app)
+ return;
+
+ if (connection->app->type == GATT_CLIENT)
+ send_client_connection_notify(connection, status);
+ else if (connection->app->type == GATT_SERVER)
+ send_server_connection_notify(connection, !status);
+}
+
+static void disconnect_notify_by_device(void *data, void *user_data)
+{
+ struct app_connection *conn = data;
+ struct gatt_device *dev = user_data;
+
+ if (dev != conn->device || !conn->app)
+ return;
+
+ if (dev->state == DEVICE_CONNECTED)
+ send_app_disconnect_notify(conn, GATT_SUCCESS);
+ else if (dev->state == DEVICE_CONNECT_INIT ||
+ dev->state == DEVICE_CONNECT_READY)
+ send_app_connect_notify(conn, GATT_FAILURE);
+}
+
static void destroy_connection(void *data)
{
struct app_connection *conn = data;
+ if (conn->timeout_id > 0)
+ g_source_remove(conn->timeout_id);
+
if (!queue_find(gatt_devices, match_by_value, conn->device))
goto cleanup;
@@ -824,6 +847,7 @@
connection_cleanup(conn->device);
cleanup:
+ queue_destroy(conn->transactions, free);
device_unref(conn->device);
free(conn);
}
@@ -838,23 +862,158 @@
destroy_connection);
}
-static void send_client_primary_notify(void *data, void *user_data)
+static gboolean disconnected_cb(GIOChannel *io, GIOCondition cond,
+ gpointer user_data)
{
- struct hal_ev_gatt_client_search_result ev;
- struct service *p = data;
- int32_t conn_id = PTR_TO_INT(user_data);
+ struct gatt_device *dev = user_data;
+ int sock, err = 0;
+ socklen_t len;
- /* In service queue we will have also included services */
- if (!p->primary)
+ sock = g_io_channel_unix_get_fd(io);
+ len = sizeof(err);
+ if (!getsockopt(sock, SOL_SOCKET, SO_ERROR, &err, &len))
+ DBG("%s (%d)", strerror(err), err);
+
+ device_disconnect_clients(dev);
+
+ return FALSE;
+}
+
+static void att_handler(const uint8_t *ipdu, uint16_t len, gpointer user_data);
+
+static void exchange_mtu_cb(guint8 status, const guint8 *pdu, guint16 plen,
+ gpointer user_data)
+{
+ struct gatt_device *device = user_data;
+ GIOChannel *io;
+ GError *gerr = NULL;
+ uint16_t rmtu, mtu, imtu;
+
+ if (status) {
+ error("gatt: MTU exchange: %s", att_ecode2str(status));
+ goto failed;
+ }
+
+ if (!dec_mtu_resp(pdu, plen, &rmtu)) {
+ error("gatt: MTU exchange: protocol error");
+ goto failed;
+ }
+
+ if (rmtu < ATT_DEFAULT_LE_MTU) {
+ error("gatt: MTU exchange: mtu error");
+ goto failed;
+ }
+
+ io = g_attrib_get_channel(device->attrib);
+
+ bt_io_get(io, &gerr, BT_IO_OPT_IMTU, &imtu, BT_IO_OPT_INVALID);
+ if (gerr) {
+ error("gatt: Could not get imtu: %s", gerr->message);
+ g_error_free(gerr);
+
+ return;
+ }
+
+ mtu = MIN(rmtu, imtu);
+ if (mtu != imtu && !g_attrib_set_mtu(device->attrib, mtu)) {
+ error("gatt: MTU exchange failed");
+ goto failed;
+ }
+
+ DBG("MTU exchange succeeded: rmtu:%d, old mtu:%d, new mtu:%d", rmtu,
+ imtu, mtu);
+
+failed:
+ device_unref(device);
+}
+
+static void send_exchange_mtu_request(struct gatt_device *device)
+{
+ GIOChannel *io;
+ GError *gerr = NULL;
+ uint16_t imtu;
+
+ io = g_attrib_get_channel(device->attrib);
+
+ bt_io_get(io, &gerr, BT_IO_OPT_IMTU, &imtu, BT_IO_OPT_INVALID);
+ if (gerr) {
+ error("gatt: Could not get imtu: %s", gerr->message);
+ g_error_free(gerr);
+
+ return;
+ }
+
+ if (!gatt_exchange_mtu(device->attrib, imtu, exchange_mtu_cb,
+ device_ref(device)))
+ device_unref(device);
+}
+
+static void notify_att_range_change(struct gatt_device *dev,
+ struct att_range *range)
+{
+ uint16_t length = 0;
+ uint16_t ccc;
+ uint8_t *pdu;
+ size_t mtu;
+
+ ccc = bt_get_gatt_ccc(&dev->bdaddr);
+ if (!ccc)
return;
- ev.conn_id = conn_id;
- element_id_to_hal_srvc_id(&p->id, 1, &ev.srvc_id);
+ pdu = g_attrib_get_buffer(dev->attrib, &mtu);
- uuid2android(&p->id.uuid, ev.srvc_id.uuid);
+ switch (ccc) {
+ case 0x0001:
+ length = enc_notification(service_changed_handle,
+ (uint8_t *) range,
+ sizeof(*range), pdu, mtu);
+ break;
+ case 0x0002:
+ length = enc_indication(service_changed_handle,
+ (uint8_t *) range, sizeof(*range), pdu,
+ mtu);
+ break;
+ default:
+ /* 0xfff4 reserved for future use */
+ break;
+ }
- ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
- HAL_EV_GATT_CLIENT_SEARCH_RESULT, sizeof(ev), &ev);
+ if (length)
+ g_attrib_send(dev->attrib, 0, pdu, length, NULL, NULL, NULL);
+}
+
+static struct app_connection *create_connection(struct gatt_device *device,
+ struct gatt_app *app)
+{
+ struct app_connection *new_conn;
+ static int32_t last_conn_id = 1;
+
+ /* Check if already connected */
+ new_conn = new0(struct app_connection, 1);
+ if (!new_conn)
+ return NULL;
+
+ /* Make connection id unique to connection record (app, device) pair */
+ new_conn->app = app;
+ new_conn->id = last_conn_id++;
+
+ new_conn->transactions = queue_new();
+ if (!new_conn->transactions) {
+ free(new_conn);
+ return NULL;
+ }
+
+ if (!queue_push_head(app_connections, new_conn)) {
+ error("gatt: Cannot push client on the client queue!?");
+ queue_destroy(new_conn->transactions, free);
+ free(new_conn);
+ return NULL;
+ }
+
+ new_conn->device = device_ref(device);
+ new_conn->device->conn_cnt++;
+
+ return new_conn;
}
static struct service *create_service(uint8_t id, bool primary, char *uuid,
@@ -904,64 +1063,252 @@
return s;
}
-static void le_device_found_handler(const bdaddr_t *addr, uint8_t addr_type,
- int rssi, uint16_t eir_len,
- const void *eir,
- bool discoverable)
+static void send_client_primary_notify(void *data, void *user_data)
{
- uint8_t buf[IPC_MTU];
- struct hal_ev_gatt_client_scan_result *ev = (void *) buf;
- struct gatt_device *dev;
- char bda[18];
+ struct hal_ev_gatt_client_search_result ev;
+ struct service *p = data;
+ int32_t conn_id = PTR_TO_INT(user_data);
- if (!scanning || !discoverable)
- goto connect;
-
- ba2str(addr, bda);
- DBG("LE Device found: %s, rssi: %d, adv_data: %d", bda, rssi, !!eir);
-
- bdaddr2android(addr, ev->bda);
- ev->rssi = rssi;
- ev->len = eir_len;
-
- memcpy(ev->adv_data, eir, ev->len);
-
- ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
- HAL_EV_GATT_CLIENT_SCAN_RESULT,
- sizeof(*ev) + ev->len, ev);
-
-connect:
- dev = find_device_by_addr(addr);
- if (!dev || (dev->state != DEVICE_CONNECT_INIT))
+ /* In service queue we will have also included services */
+ if (!p->primary)
return;
- device_set_state(dev, DEVICE_CONNECT_READY);
- dev->bdaddr_type = addr_type;
+ ev.conn_id = conn_id;
+ element_id_to_hal_srvc_id(&p->id, 1, &ev.srvc_id);
- /*
- * We are ok to perform connect now. Stop discovery
- * and once it is stopped continue with creating ACL
- */
- bt_le_discovery_stop(bt_le_discovery_stop_cb);
+ uuid2android(&p->id.uuid, ev.srvc_id.uuid);
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_EV_GATT_CLIENT_SEARCH_RESULT, sizeof(ev), &ev);
}
-static gboolean disconnected_cb(GIOChannel *io, GIOCondition cond,
- gpointer user_data)
+static void send_client_search_complete_notify(int32_t status, int32_t conn_id)
{
- struct gatt_device *dev = user_data;
- int sock, err = 0;
- socklen_t len;
+ struct hal_ev_gatt_client_search_complete ev;
- sock = g_io_channel_unix_get_fd(io);
- len = sizeof(err);
- if (!getsockopt(sock, SOL_SOCKET, SO_ERROR, &err, &len))
- DBG("%s (%d)", strerror(err), err);
+ ev.status = status;
+ ev.conn_id = conn_id;
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_EV_GATT_CLIENT_SEARCH_COMPLETE, sizeof(ev), &ev);
+}
- device_disconnect_clients(dev);
+struct discover_srvc_data {
+ bt_uuid_t uuid;
+ struct app_connection *conn;
+};
+
+static void discover_srvc_by_uuid_cb(uint8_t status, GSList *ranges,
+ void *user_data)
+{
+ struct discover_srvc_data *cb_data = user_data;
+ struct gatt_primary prim;
+ struct service *s;
+ int32_t gatt_status;
+ struct gatt_device *dev = cb_data->conn->device;
+ uint8_t instance_id = queue_length(dev->services);
+
+ DBG("Status %d", status);
+
+ if (status) {
+ error("gatt: Discover pri srvc filtered by uuid failed: %s",
+ att_ecode2str(status));
+ gatt_status = GATT_FAILURE;
+ goto reply;
+ }
+
+ if (!ranges) {
+ info("gatt: No primary services searched by uuid found");
+ gatt_status = GATT_SUCCESS;
+ goto reply;
+ }
+
+ bt_uuid_to_string(&cb_data->uuid, prim.uuid, sizeof(prim.uuid));
+ /*
+ * If multiple instances of the same service (as identified by UUID)
+ * exist, the first instance of the service is returned.
+ */
+ memcpy(&prim.range, ranges->data, sizeof(prim.range));
+
+ s = create_service(instance_id++, true, prim.uuid, &prim);
+ if (!s) {
+ gatt_status = GATT_FAILURE;
+ goto reply;
+ }
+
+ if (!queue_push_tail(dev->services, s)) {
+ error("gatt: Cannot push primary service to the list");
+ gatt_status = GATT_FAILURE;
+ goto reply;
+ }
+
+ send_client_primary_notify(s, INT_TO_PTR(cb_data->conn->id));
+
+ DBG("attr handle = 0x%04x, end grp handle = 0x%04x uuid: %s",
+ prim.range.start, prim.range.end, prim.uuid);
+
+ /* Partial search service scanning was performed */
+ dev->partial_srvc_search = true;
+ gatt_status = GATT_SUCCESS;
+
+reply:
+ send_client_search_complete_notify(gatt_status, cb_data->conn->id);
+ free(cb_data);
+}
+
+static void discover_srvc_all_cb(uint8_t status, GSList *services,
+ void *user_data)
+{
+ struct discover_srvc_data *cb_data = user_data;
+ struct gatt_device *dev = cb_data->conn->device;
+ int32_t gatt_status;
+ GSList *l;
+ /*
+ * There might be multiply services with same uuid. Therefore make sure
+ * each primary service one has unique instance_id
+ */
+ uint8_t instance_id = queue_length(dev->services);
+
+ DBG("Status %d", status);
+
+ if (status) {
+ error("gatt: Discover all primary services failed: %s",
+ att_ecode2str(status));
+ gatt_status = GATT_FAILURE;
+ goto reply;
+ }
+
+ if (!services) {
+ info("gatt: No primary services found");
+ gatt_status = GATT_SUCCESS;
+ goto reply;
+ }
+
+ for (l = services; l; l = l->next) {
+ struct gatt_primary *prim = l->data;
+ struct service *p;
+
+ if (queue_find(dev->services, match_srvc_by_range,
+ &prim->range))
+ continue;
+
+ p = create_service(instance_id++, true, prim->uuid, prim);
+ if (!p)
+ continue;
+
+ if (!queue_push_tail(dev->services, p)) {
+ error("gatt: Cannot push primary service to the list");
+ free(p);
+ continue;
+ }
+
+ DBG("attr handle = 0x%04x, end grp handle = 0x%04x uuid: %s",
+ prim->range.start, prim->range.end, prim->uuid);
+ }
+
+ /*
+ * Send all found services notifications - first cache,
+ * then send notifies
+ */
+ queue_foreach(dev->services, send_client_primary_notify,
+ INT_TO_PTR(cb_data->conn->id));
+
+ /* Full search service scanning was performed */
+ dev->partial_srvc_search = false;
+ gatt_status = GATT_SUCCESS;
+
+reply:
+ send_client_search_complete_notify(gatt_status, cb_data->conn->id);
+ free(cb_data);
+}
+static gboolean connection_timeout(void *user_data)
+{
+ struct app_connection *conn = user_data;
+
+ conn->timeout_id = 0;
+
+ queue_remove(app_connections, conn);
+ destroy_connection(conn);
return FALSE;
}
+static void discover_primary_cb(uint8_t status, GSList *services,
+ void *user_data)
+{
+ struct discover_srvc_data *cb_data = user_data;
+ struct app_connection *conn = cb_data->conn;
+ struct gatt_device *dev = conn->device;
+ GSList *l, *uuids = NULL;
+
+ DBG("Status %d", status);
+
+ if (status) {
+ error("gatt: Discover all primary services failed: %s",
+ att_ecode2str(status));
+ free(cb_data);
+
+ return;
+ }
+
+ if (!services) {
+ info("gatt: No primary services found");
+ free(cb_data);
+
+ return;
+ }
+
+ for (l = services; l; l = l->next) {
+ struct gatt_primary *prim = l->data;
+ uint8_t *new_uuid;
+ bt_uuid_t uuid;
+
+ DBG("uuid: %s", prim->uuid);
+
+ if (bt_string_to_uuid(&uuid, prim->uuid) < 0) {
+ error("gatt: Cannot convert string to uuid");
+ continue;
+ }
+
+ new_uuid = g_memdup(&uuid.value.u128, sizeof(uuid.value.u128));
+
+ uuids = g_slist_prepend(uuids, new_uuid);
+ }
+
+ bt_device_set_uuids(&dev->bdaddr, uuids);
+
+ free(cb_data);
+
+ conn->timeout_id = g_timeout_add_seconds(GATT_CONN_TIMEOUT,
+ connection_timeout, conn);
+}
+
+static guint search_dev_for_srvc(struct app_connection *conn, bt_uuid_t *uuid)
+{
+ struct discover_srvc_data *cb_data =
+ new0(struct discover_srvc_data, 1);
+
+ if (!cb_data) {
+ error("gatt: Cannot allocate cb data");
+ return 0;
+ }
+
+ cb_data->conn = conn;
+
+ if (uuid) {
+ memcpy(&cb_data->uuid, uuid, sizeof(cb_data->uuid));
+ return gatt_discover_primary(conn->device->attrib, uuid,
+ discover_srvc_by_uuid_cb, cb_data);
+ }
+
+
+ if (conn->app)
+ return gatt_discover_primary(conn->device->attrib, NULL,
+ discover_srvc_all_cb, cb_data);
+
+ return gatt_discover_primary(conn->device->attrib, NULL,
+ discover_primary_cb, cb_data);
+}
+
struct connect_data {
struct gatt_device *dev;
int32_t status;
@@ -976,12 +1323,11 @@
send_app_connect_notify(conn, con_data->status);
}
-static void att_handler(const uint8_t *ipdu, uint16_t len, gpointer user_data);
-
static void connect_cb(GIOChannel *io, GError *gerr, gpointer user_data)
{
struct gatt_device *dev = user_data;
struct connect_data data;
+ struct att_range range;
uint32_t status;
GAttrib *attrib;
@@ -991,8 +1337,10 @@
return;
}
- g_io_channel_unref(dev->att_io);
- dev->att_io = NULL;
+ if (dev->att_io) {
+ g_io_channel_unref(dev->att_io);
+ dev->att_io = NULL;
+ }
if (gerr) {
error("gatt: connection failed %s", gerr->message);
@@ -1021,9 +1369,42 @@
device_set_state(dev, DEVICE_CONNECTED);
+ /* Send exchange mtu request as we assume being client and server */
+ /* TODO: Dont exchange mtu if no client apps */
+ send_exchange_mtu_request(dev);
+
+ /*
+ * Service Changed Characteristic and CCC Descriptor handles
+ * should not change if there are bonded devices. We have them
+ * constant all the time, thus they should be excluded from
+ * range indicating changes.
+ */
+ range.start = service_changed_handle + 2;
+ range.end = 0xffff;
+
+ /*
+ * If there is ccc stored for that device we were acting as server for
+ * it, and as we dont have last connect and last services (de)activation
+ * timestamps we should always assume something has changed.
+ */
+ notify_att_range_change(dev, &range);
+
status = GATT_SUCCESS;
reply:
+ if (!dev->conn_cnt) {
+ struct app_connection *conn;
+
+ if (!dev->attrib)
+ return;
+
+ conn = create_connection(dev, NULL);
+ if (!conn)
+ return;
+
+ search_dev_for_srvc(conn, NULL);
+ }
+
data.dev = dev;
data.status = status;
queue_foreach(app_connections, send_app_connect_notifications, &data);
@@ -1031,7 +1412,7 @@
/* Check if we should restart scan */
if (scanning)
- bt_le_discovery_start(le_device_found_handler);
+ bt_le_discovery_start();
/* FIXME: What to do if discovery won't start here. */
}
@@ -1053,8 +1434,8 @@
DBG("Connection attempt to: %s", addr);
- /* TODO: If we are bonded then we should use higier sec level */
- sec_level = BT_IO_SEC_LOW;
+ sec_level = bt_device_is_bonded(&dev->bdaddr) ? BT_IO_SEC_MEDIUM :
+ BT_IO_SEC_LOW;
/*
* This connection will help us catch any PDUs that comes before
@@ -1082,6 +1463,142 @@
return 0;
}
+static int connect_next_dev(void)
+{
+ struct gatt_device *dev;
+
+ DBG("");
+
+ dev = find_device_by_state(DEVICE_CONNECT_READY);
+ if (!dev)
+ return -ENODEV;
+
+ return connect_le(dev);
+}
+
+static void le_device_found_handler(const bdaddr_t *addr, uint8_t addr_type,
+ int rssi, uint16_t eir_len,
+ const void *eir,
+ bool discoverable, bool bonded)
+{
+ uint8_t buf[IPC_MTU];
+ struct hal_ev_gatt_client_scan_result *ev = (void *) buf;
+ struct gatt_device *dev;
+ char bda[18];
+
+ if (!scanning || (!discoverable && !bonded))
+ goto connect;
+
+ ba2str(addr, bda);
+ DBG("LE Device found: %s, rssi: %d, adv_data: %d", bda, rssi, !!eir);
+
+ bdaddr2android(addr, ev->bda);
+ ev->rssi = rssi;
+ ev->len = eir_len;
+
+ memcpy(ev->adv_data, eir, ev->len);
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_EV_GATT_CLIENT_SCAN_RESULT,
+ sizeof(*ev) + ev->len, ev);
+
+connect:
+ dev = find_device_by_addr(addr);
+ if (!dev) {
+ if (!bonded)
+ return;
+
+ dev = create_device(addr);
+ }
+
+ if (!dev || dev->state != DEVICE_CONNECT_INIT)
+ return;
+
+ device_set_state(dev, DEVICE_CONNECT_READY);
+ dev->bdaddr_type = addr_type;
+
+ /*
+ * We are ok to perform connect now. Stop discovery
+ * and once it is stopped continue with creating ACL
+ */
+ bt_le_discovery_stop(bt_le_discovery_stop_cb);
+}
+
+static struct gatt_app *register_app(const uint8_t *uuid, gatt_type_t type)
+{
+ static int32_t application_id = 1;
+ struct gatt_app *app;
+
+ if (queue_find(gatt_apps, match_app_by_uuid, uuid)) {
+ error("gatt: app uuid is already on list");
+ return NULL;
+ }
+
+ app = new0(struct gatt_app, 1);
+ if (!app) {
+ error("gatt: Cannot allocate memory for registering app");
+ return 0;
+ }
+
+ app->type = type;
+
+ if (app->type == GATT_CLIENT) {
+ app->notifications = queue_new();
+ if (!app->notifications) {
+ error("gatt: couldn't allocate notifications queue");
+ destroy_gatt_app(app);
+ return NULL;
+ }
+ }
+
+ memcpy(app->uuid, uuid, sizeof(app->uuid));
+
+ app->id = application_id++;
+
+ if (!queue_push_head(gatt_apps, app)) {
+ error("gatt: Cannot push app on the list");
+ destroy_gatt_app(app);
+ return NULL;
+ }
+
+ if ((app->type == GATT_SERVER) &&
+ !queue_push_tail(listen_apps, INT_TO_PTR(app->id))) {
+ error("gatt: Cannot push server on the list");
+ destroy_gatt_app(app);
+ return NULL;
+ }
+
+ return app;
+}
+
+static void handle_client_register(const void *buf, uint16_t len)
+{
+ const struct hal_cmd_gatt_client_register *cmd = buf;
+ struct hal_ev_gatt_client_register_client ev;
+ struct gatt_app *app;
+
+ DBG("");
+
+ memset(&ev, 0, sizeof(ev));
+
+ app = register_app(cmd->uuid, GATT_CLIENT);
+
+ if (app) {
+ ev.client_if = app->id;
+ ev.status = GATT_SUCCESS;
+ } else
+ ev.status = GATT_FAILURE;
+
+ /* We should send notification with given in cmd UUID */
+ memcpy(ev.app_uuid, cmd->uuid, sizeof(ev.app_uuid));
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_EV_GATT_CLIENT_REGISTER_CLIENT, sizeof(ev), &ev);
+
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_REGISTER,
+ HAL_STATUS_SUCCESS);
+}
+
static void handle_client_scan(const void *buf, uint16_t len)
{
const struct hal_cmd_gatt_client_scan *cmd = buf;
@@ -1117,7 +1634,7 @@
}
/* Turn on scan */
- if (!bt_le_discovery_start(le_device_found_handler)) {
+ if (!bt_le_discovery_start()) {
error("gatt: LE scan switch failed");
status = HAL_STATUS_FAILED;
goto reply;
@@ -1130,87 +1647,13 @@
status);
}
-static int connect_next_dev(void)
-{
- struct gatt_device *dev;
-
- DBG("");
-
- dev = find_device_by_state(DEVICE_CONNECT_READY);
- if (!dev)
- return -ENODEV;
-
- return connect_le(dev);
-}
-
static void bt_le_discovery_stop_cb(void)
{
DBG("");
/* Check now if there is any device ready to connect */
if (connect_next_dev() < 0)
- bt_le_discovery_start(le_device_found_handler);
-}
-
-static struct gatt_device *create_device(const bdaddr_t *addr)
-{
- struct gatt_device *dev;
-
- dev = new0(struct gatt_device, 1);
- if (!dev)
- return NULL;
-
- bacpy(&dev->bdaddr, addr);
-
- dev->services = queue_new();
- if (!dev->services) {
- error("gatt: Failed to allocate memory for client");
- destroy_device(dev);
- return NULL;
- }
-
- dev->pending_requests = queue_new();
- if (!dev->pending_requests) {
- error("gatt: Failed to allocate memory for client");
- destroy_device(dev);
- return NULL;
- }
-
- if (!queue_push_head(gatt_devices, dev)) {
- error("gatt: Cannot push device to queue");
- destroy_device(dev);
- return NULL;
- }
-
- return device_ref(dev);
-}
-
-static struct app_connection *create_connection(struct gatt_device *device,
- struct gatt_app *app)
-{
- struct app_connection *new_conn;
- static int32_t last_conn_id = 1;
-
- /* Check if already connected */
- new_conn = new0(struct app_connection, 1);
- if (!new_conn)
- return NULL;
-
- /* Make connection id unique to connection record (app, device) pair */
- new_conn->app = app;
- new_conn->id = last_conn_id++;
-
- if (!queue_push_head(app_connections, new_conn)) {
- error("gatt: Cannot push client on the client queue!?");
-
- free(new_conn);
- return NULL;
- }
-
- new_conn->device = device_ref(device);
- new_conn->device->conn_cnt++;
-
- return new_conn;
+ bt_le_discovery_start();
}
static void trigger_disconnection(struct app_connection *connection)
@@ -1252,7 +1695,7 @@
/* after state change trigger discovering */
if (!scanning && (connection->device->state == DEVICE_CONNECT_INIT))
- if (!bt_le_discovery_start(le_device_found_handler)) {
+ if (!bt_le_discovery_start()) {
error("gatt: Could not start scan");
return false;
@@ -1261,28 +1704,32 @@
return true;
}
+static uint8_t unregister_app(int client_if)
+{
+ struct gatt_app *cl;
+
+ cl = queue_remove_if(gatt_apps, match_app_by_id, INT_TO_PTR(client_if));
+ if (!cl) {
+ error("gatt: client_if=%d not found", client_if);
+
+ return HAL_STATUS_FAILED;
+ }
+
+ /* Destroy app connections with proper notifications for this app. */
+ app_disconnect_devices(cl);
+ destroy_gatt_app(cl);
+
+ return HAL_STATUS_SUCCESS;
+}
+
static void handle_client_unregister(const void *buf, uint16_t len)
{
const struct hal_cmd_gatt_client_unregister *cmd = buf;
uint8_t status;
- struct gatt_app *cl;
DBG("");
- cl = queue_remove_if(gatt_apps, match_app_by_id,
- INT_TO_PTR(cmd->client_if));
- if (!cl) {
- error("gatt: client_if=%d not found", cmd->client_if);
- status = HAL_STATUS_FAILED;
- } else {
- /*
- * Check if there is any connect request or connected device
- * for this client. If so, remove this client from those lists.
- */
- app_disconnect_devices(cl);
- destroy_gatt_app(cl);
- status = HAL_STATUS_SUCCESS;
- }
+ status = unregister_app(cmd->client_if);
ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
HAL_OP_GATT_CLIENT_UNREGISTER, status);
@@ -1556,167 +2003,6 @@
status);
}
-struct discover_srvc_data {
- bt_uuid_t uuid;
- struct app_connection *conn;
-};
-
-static void send_client_search_complete_notify(int32_t status, int32_t conn_id)
-{
- struct hal_ev_gatt_client_search_complete ev;
-
- ev.status = status;
- ev.conn_id = conn_id;
- ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
- HAL_EV_GATT_CLIENT_SEARCH_COMPLETE, sizeof(ev), &ev);
-}
-
-static void discover_srvc_all_cb(uint8_t status, GSList *services,
- void *user_data)
-{
- struct discover_srvc_data *cb_data = user_data;
- struct gatt_device *dev = cb_data->conn->device;
- int32_t gatt_status;
- GSList *l;
- /*
- * There might be multiply services with same uuid. Therefore make sure
- * each primary service one has unique instance_id
- */
- uint8_t instance_id = queue_length(dev->services);
-
- DBG("Status %d", status);
-
- if (status) {
- error("gatt: Discover all primary services failed: %s",
- att_ecode2str(status));
- gatt_status = GATT_FAILURE;
- goto reply;
- }
-
- if (!services) {
- info("gatt: No primary services found");
- gatt_status = GATT_SUCCESS;
- goto reply;
- }
-
- for (l = services; l; l = l->next) {
- struct gatt_primary *prim = l->data;
- struct service *p;
-
- if (queue_find(dev->services, match_srvc_by_range,
- &prim->range))
- continue;
-
- p = create_service(instance_id++, true, prim->uuid, prim);
- if (!p)
- continue;
-
- if (!queue_push_tail(dev->services, p)) {
- error("gatt: Cannot push primary service to the list");
- free(p);
- continue;
- }
-
- DBG("attr handle = 0x%04x, end grp handle = 0x%04x uuid: %s",
- prim->range.start, prim->range.end, prim->uuid);
- }
-
- /*
- * Send all found services notifications - first cache,
- * then send notifies
- */
- queue_foreach(dev->services, send_client_primary_notify,
- INT_TO_PTR(cb_data->conn->id));
-
- /* Full search service scanning was performed */
- dev->partial_srvc_search = false;
- gatt_status = GATT_SUCCESS;
-
-reply:
- send_client_search_complete_notify(gatt_status, cb_data->conn->id);
- free(cb_data);
-}
-
-static void discover_srvc_by_uuid_cb(uint8_t status, GSList *ranges,
- void *user_data)
-{
- struct discover_srvc_data *cb_data = user_data;
- struct gatt_primary prim;
- struct service *s;
- int32_t gatt_status;
- struct gatt_device *dev = cb_data->conn->device;
- uint8_t instance_id = queue_length(dev->services);
-
- DBG("Status %d", status);
-
- if (status) {
- error("gatt: Discover pri srvc filtered by uuid failed: %s",
- att_ecode2str(status));
- gatt_status = GATT_FAILURE;
- goto reply;
- }
-
- if (!ranges) {
- info("gatt: No primary services searched by uuid found");
- gatt_status = GATT_SUCCESS;
- goto reply;
- }
-
- bt_uuid_to_string(&cb_data->uuid, prim.uuid, sizeof(prim.uuid));
- /*
- * If multiple instances of the same service (as identified by UUID)
- * exist, the first instance of the service is returned.
- */
- memcpy(&prim.range, ranges->data, sizeof(prim.range));
-
- s = create_service(instance_id++, true, prim.uuid, &prim);
- if (!s) {
- gatt_status = GATT_FAILURE;
- goto reply;
- }
-
- if (!queue_push_tail(dev->services, s)) {
- error("gatt: Cannot push primary service to the list");
- gatt_status = GATT_FAILURE;
- goto reply;
- }
-
- send_client_primary_notify(s, INT_TO_PTR(cb_data->conn->id));
-
- DBG("attr handle = 0x%04x, end grp handle = 0x%04x uuid: %s",
- prim.range.start, prim.range.end, prim.uuid);
-
- /* Partial search service scanning was performed */
- dev->partial_srvc_search = true;
- gatt_status = GATT_SUCCESS;
-
-reply:
- send_client_search_complete_notify(gatt_status, cb_data->conn->id);
- free(cb_data);
-}
-
-static guint search_dev_for_srvc(struct app_connection *conn, bt_uuid_t *uuid)
-{
- struct discover_srvc_data *cb_data =
- new0(struct discover_srvc_data, 1);
-
- if (!cb_data) {
- error("gatt: Cannot allocate cb data");
- return 0;
- }
-
- cb_data->conn = conn;
-
- if (uuid) {
- memcpy(&cb_data->uuid, uuid, sizeof(cb_data->uuid));
- return gatt_discover_primary(conn->device->attrib, uuid,
- discover_srvc_by_uuid_cb, cb_data);
- }
-
- return gatt_discover_primary(conn->device->attrib, NULL,
- discover_srvc_all_cb, cb_data);
-}
-
static void handle_client_search_service(const void *buf, uint16_t len)
{
const struct hal_cmd_gatt_client_search_service *cmd = buf;
@@ -1781,8 +2067,10 @@
if (s) {
send_client_primary_notify(s, INT_TO_PTR(conn->id));
} else {
- if (!search_dev_for_srvc(conn, &uuid))
+ if (!search_dev_for_srvc(conn, &uuid)) {
status = HAL_STATUS_FAILED;
+ goto reply;
+ }
status = HAL_STATUS_SUCCESS;
goto reply;
@@ -2080,6 +2368,9 @@
srvc->incl.range.end;
}
+ DBG("attr handle = 0x%04x, end handle = 0x%04x uuid: %s",
+ ch->ch.handle, ch->end_handle, ch->ch.uuid);
+
if (!queue_push_tail(srvc->chars, ch)) {
error("gatt: Error while caching characteristic");
destroy_characteristic(ch);
@@ -2236,6 +2527,8 @@
descr->id.instance = i++;
descr->handle = desc->handle;
+ DBG("attr handle = 0x%04x, uuid: %s", desc->handle, desc->uuid);
+
if (!queue_push_tail(ch->descriptors, descr))
free(descr);
}
@@ -2426,6 +2719,65 @@
free(data);
}
+static int get_sec_level(struct gatt_device *dev)
+{
+ GIOChannel *io;
+ int sec_level;
+
+ io = g_attrib_get_channel(dev->attrib);
+
+ if (!bt_io_get(io, NULL, BT_IO_OPT_SEC_LEVEL, &sec_level,
+ BT_IO_OPT_INVALID)) {
+ error("gatt: Failed to get sec_level");
+ return -1;
+ }
+
+ return sec_level;
+}
+
+static bool set_security(struct gatt_device *device, int auth_type)
+{
+ int req_sec_level, sec_level;
+ GError *gerr = NULL;
+ GIOChannel *io;
+
+ switch (auth_type) {
+ case HAL_GATT_AUTHENTICATION_MITM:
+ req_sec_level = BT_SECURITY_HIGH;
+ break;
+ case HAL_GATT_AUTHENTICATION_NO_MITM:
+ req_sec_level = BT_SECURITY_MEDIUM;
+ break;
+ case HAL_GATT_AUTHENTICATION_NONE:
+ req_sec_level = BT_SECURITY_LOW;
+ break;
+ default:
+ error("gatt: Invalid auth_type value: %d", auth_type);
+ return false;
+ }
+
+ sec_level = get_sec_level(device);
+ if (sec_level < 0)
+ return false;
+
+ if (req_sec_level <= sec_level)
+ return true;
+
+ io = g_attrib_get_channel(device->attrib);
+ if (!io)
+ return false;
+
+ bt_io_set(io, &gerr, BT_IO_OPT_SEC_LEVEL, req_sec_level,
+ BT_IO_OPT_INVALID);
+ if (gerr) {
+ error("gatt: Failed to set security level: %s", gerr->message);
+ g_error_free(gerr);
+ return false;
+ }
+
+ return true;
+}
+
static void handle_client_read_characteristic(const void *buf, uint16_t len)
{
const struct hal_cmd_gatt_client_read_characteristic *cmd = buf;
@@ -2466,6 +2818,13 @@
goto failed;
}
+ if (!set_security(conn->device, cmd->auth_req)) {
+ error("gatt: Failed to set security %d", cmd->auth_req);
+ status = HAL_STATUS_FAILED;
+ free(cb_data);
+ goto failed;
+ }
+
if (!gatt_read_char(conn->device->attrib, ch->ch.value_handle,
read_char_cb, cb_data)) {
error("gatt: Cannot read characteristic with inst_id: %d",
@@ -2522,6 +2881,30 @@
free(data);
}
+static bool signed_write_cmd(struct gatt_device *dev, uint16_t handle,
+ const uint8_t *value, uint16_t vlen)
+{
+ uint8_t csrk[16];
+ uint32_t sign_cnt;
+
+ memset(csrk, 0, 16);
+
+ if (!bt_get_csrk(&dev->bdaddr, LOCAL_CSRK, csrk, &sign_cnt)) {
+ error("gatt: Could not get csrk key");
+ return false;
+ }
+
+ if (!gatt_signed_write_cmd(dev->attrib, handle, value, vlen, crypto,
+ csrk, sign_cnt, NULL, NULL)) {
+ error("gatt: Could write signed cmd");
+ return false;
+ }
+
+ bt_update_sign_counter(&dev->bdaddr, LOCAL_CSRK);
+
+ return true;
+}
+
static void handle_client_write_characteristic(const void *buf, uint16_t len)
{
const struct hal_cmd_gatt_client_write_characteristic *cmd = buf;
@@ -2569,6 +2952,12 @@
}
}
+ if (!set_security(conn->device, cmd->auth_req)) {
+ error("gatt: Failed to set security %d", cmd->auth_req);
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
switch (cmd->write_type) {
case GATT_WRITE_TYPE_NO_RESPONSE:
res = gatt_write_cmd(conn->device->attrib, ch->ch.value_handle,
@@ -2586,6 +2975,16 @@
cmd->value, cmd->len,
write_char_cb, cb_data);
break;
+ case GATT_WRITE_TYPE_SIGNED:
+ if (get_sec_level(conn->device) != BT_SECURITY_LOW) {
+ error("gatt: Cannot write signed on encrypted link");
+ res = HAL_STATUS_FAILED;
+ } else {
+ res = signed_write_cmd(conn->device,
+ ch->ch.value_handle,
+ cmd->value, cmd->len);
+ }
+ break;
default:
error("gatt: Write type %d unsupported", cmd->write_type);
status = HAL_STATUS_UNSUPPORTED;
@@ -2760,6 +3159,13 @@
goto failed;
}
+ if (!set_security(conn->device, cmd->auth_req)) {
+ error("gatt: Failed to set security %d", cmd->auth_req);
+ status = HAL_STATUS_FAILED;
+ free(cb_data);
+ goto failed;
+ }
+
if (!gatt_read_char(conn->device->attrib, descr->handle, read_desc_cb,
cb_data)) {
free(cb_data);
@@ -2885,6 +3291,12 @@
}
}
+ if (!set_security(conn->device, cmd->auth_req)) {
+ error("gatt: Failed to set security %d", cmd->auth_req);
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+
switch (cmd->write_type) {
case GATT_WRITE_TYPE_NO_RESPONSE:
res = gatt_write_cmd(conn->device->attrib, descr->handle,
@@ -3004,20 +3416,6 @@
ev->len = len - data_offset;
memcpy(ev->value, pdu + data_offset, len - data_offset);
- if (!ev->is_notify) {
- uint8_t *res;
- uint16_t len;
- size_t plen;
-
- res = g_attrib_get_buffer(
- notification->conn->device->attrib,
- &plen);
- len = enc_confirmation(res, plen);
- if (len > 0)
- g_attrib_send(notification->conn->device->attrib,
- 0, res, len, NULL, NULL, NULL);
- }
-
ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, HAL_EV_GATT_CLIENT_NOTIFY,
sizeof(*ev) + ev->len, ev);
}
@@ -3279,19 +3677,18 @@
return;
}
- DBG("scan_rsp=%u name=%u tx=%u min=%d max=%d app=%d manufacturer=%u",
+ DBG("scan_rsp=%u name=%u tx=%u min=%d max=%d app=%d",
cmd->set_scan_rsp, cmd->include_name, cmd->include_txpower,
- cmd->min_interval, cmd->max_interval, cmd->appearance,
- cmd->manufacturer_len);
+ cmd->min_interval, cmd->max_interval, cmd->appearance);
- /*
- * TODO
- * Currently kernel is setting all except for vendor data.
- * This should be implemented when kernel supports it.
- */
+ DBG("manufacturer=%u service_data=%u service_uuid=%u",
+ cmd->manufacturer_len, cmd->service_data_len,
+ cmd->service_uuid_len);
- if (cmd->manufacturer_len) {
- error("gatt: Manufacturer advertising data not supported");
+ /* TODO This should be implemented when kernel supports it */
+ if (cmd->manufacturer_len || cmd->service_data_len ||
+ cmd->service_uuid_len) {
+ error("gatt: Extra advertising data not supported");
status = HAL_STATUS_FAILED;
goto failed;
}
@@ -3303,29 +3700,168 @@
HAL_OP_GATT_CLIENT_SET_ADV_DATA, status);
}
+static uint8_t test_read_write(bdaddr_t *bdaddr, bt_uuid_t *uuid, uint16_t op,
+ uint16_t u2,uint16_t u3,
+ uint16_t u4, uint16_t u5)
+{
+ guint16 length = 0;
+ struct gatt_device *dev;
+ uint8_t *pdu;
+ size_t mtu;
+
+ dev = find_device_by_addr(bdaddr);
+ if (!dev || dev->state != DEVICE_CONNECTED)
+ return HAL_STATUS_FAILED;
+
+ pdu = g_attrib_get_buffer(dev->attrib, &mtu);
+ if (!pdu)
+ return HAL_STATUS_FAILED;
+
+ switch (op) {
+ case ATT_OP_READ_REQ:
+ length = enc_read_req(u2, pdu, mtu);
+ break;
+ case ATT_OP_READ_BY_TYPE_REQ:
+ length = enc_read_by_type_req(u2, u3, uuid, pdu, mtu);
+ break;
+ case ATT_OP_READ_BLOB_REQ:
+ length = enc_read_blob_req(u2, u3, pdu, mtu);
+ break;
+ case ATT_OP_READ_BY_GROUP_REQ:
+ length = enc_read_by_grp_req(u2, u3, uuid, pdu, mtu);
+ break;
+ case ATT_OP_READ_MULTI_REQ:
+ return HAL_STATUS_UNSUPPORTED;
+ case ATT_OP_WRITE_REQ:
+ length = enc_write_req(u2, (uint8_t *) &u3, sizeof(u3), pdu,
+ mtu);
+ break;
+ case ATT_OP_WRITE_CMD:
+ length = enc_write_cmd(u2, (uint8_t *) &u3, sizeof(u3), pdu,
+ mtu);
+ break;
+ case ATT_OP_PREP_WRITE_REQ:
+ length = enc_prep_write_req(u2, u3, (uint8_t *) &u4, sizeof(u4),
+ pdu, mtu);
+ break;
+ case ATT_OP_EXEC_WRITE_REQ:
+ length = enc_exec_write_req(u2, pdu, mtu);
+ break;
+ case ATT_OP_SIGNED_WRITE_CMD:
+ if (signed_write_cmd(dev, u2, (uint8_t *) &u3, sizeof(u3)))
+ return HAL_STATUS_SUCCESS;
+ else
+ return HAL_STATUS_FAILED;
+ default:
+ error("gatt: Unknown operation type");
+
+ return HAL_STATUS_UNSUPPORTED;
+ }
+
+ if (!length)
+ return HAL_STATUS_FAILED;
+
+ g_attrib_send(dev->attrib, 0, pdu, length, NULL, NULL, NULL);
+
+ return HAL_STATUS_SUCCESS;
+}
+
+static uint8_t test_increase_security(bdaddr_t *bdaddr, uint16_t u1)
+{
+ struct gatt_device *device;
+
+ device = find_device_by_addr(bdaddr);
+ if (!device)
+ return HAL_STATUS_FAILED;
+
+ if (!set_security(device, u1))
+ return HAL_STATUS_FAILED;
+
+ return HAL_STATUS_SUCCESS;
+}
+
static void handle_client_test_command(const void *buf, uint16_t len)
{
+ const struct hal_cmd_gatt_client_test_command *cmd = buf;
+ struct gatt_app *app;
+ bdaddr_t bdaddr;
+ bt_uuid_t uuid;
+ uint8_t status;
+
DBG("");
+ android2bdaddr(cmd->bda1, &bdaddr);
+ android2uuid(cmd->uuid1, &uuid);
+
+ switch (cmd->command) {
+ case GATT_CLIENT_TEST_CMD_ENABLE:
+ if (cmd->u1) {
+ if (!test_client_if) {
+ app = register_app(TEST_UUID, GATT_CLIENT);
+ if (app)
+ test_client_if = app->id;
+ }
+
+ if (test_client_if)
+ status = HAL_STATUS_SUCCESS;
+ else
+ status = HAL_STATUS_FAILED;
+ } else {
+ status = unregister_app(test_client_if);
+ test_client_if = 0;
+ }
+ break;
+ case GATT_CLIENT_TEST_CMD_CONNECT:
+ /* TODO u1 holds device type, for now assume BLE */
+ status = handle_connect(test_client_if, &bdaddr);
+ break;
+ case GATT_CLIENT_TEST_CMD_DISCONNECT:
+ app = queue_find(gatt_apps, match_app_by_id,
+ INT_TO_PTR(test_client_if));
+ if (app)
+ app_disconnect_devices(app);
+
+ status = HAL_STATUS_SUCCESS;
+ break;
+ case GATT_CLIENT_TEST_CMD_DISCOVER:
+ status = HAL_STATUS_FAILED;
+ break;
+ case GATT_CLIENT_TEST_CMD_READ:
+ case GATT_CLIENT_TEST_CMD_WRITE:
+ status = test_read_write(&bdaddr, &uuid, cmd->u1, cmd->u2,
+ cmd->u3, cmd->u4, cmd->u5);
+ break;
+ case GATT_CLIENT_TEST_CMD_INCREASE_SECURITY:
+ status = test_increase_security(&bdaddr, cmd->u1);
+ break;
+ case GATT_CLIENT_TEST_CMD_PAIRING_CONFIG:
+ default:
+ status = HAL_STATUS_FAILED;
+ break;
+ }
+
ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
- HAL_OP_GATT_CLIENT_TEST_COMMAND, HAL_STATUS_FAILED);
+ HAL_OP_GATT_CLIENT_TEST_COMMAND, status);
}
static void handle_server_register(const void *buf, uint16_t len)
{
const struct hal_cmd_gatt_server_register *cmd = buf;
struct hal_ev_gatt_server_register ev;
+ struct gatt_app *app;
DBG("");
memset(&ev, 0, sizeof(ev));
- ev.server_if = register_app(cmd->uuid, APP_SERVER);
+ app = register_app(cmd->uuid, GATT_SERVER);
- if (ev.server_if)
+ if (app) {
+ ev.server_if = app->id;
ev.status = GATT_SUCCESS;
- else
+ } else {
ev.status = GATT_FAILURE;
+ }
memcpy(ev.uuid, cmd->uuid, sizeof(ev.uuid));
@@ -3340,21 +3876,11 @@
{
const struct hal_cmd_gatt_server_unregister *cmd = buf;
uint8_t status;
- struct gatt_app *server;
DBG("");
- server = find_app_by_id(cmd->server_if);
- if (!server) {
- error("gatt: server_if=%d not found", cmd->server_if);
- status = HAL_STATUS_FAILED;
- goto failed;
- }
+ status = unregister_app(cmd->server_if);
- destroy_gatt_app(server);
- status = HAL_STATUS_SUCCESS;
-
-failed:
ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
HAL_OP_GATT_SERVER_UNREGISTER, status);
}
@@ -3490,14 +4016,28 @@
return false;
}
-static void send_dev_pending_response(struct gatt_device *device,
+static bool match_pending_dev_request(const void *data, const void *user_data)
+{
+ const struct pending_request *pending_request = data;
+
+ return pending_request->state == REQUEST_PENDING;
+}
+
+static void send_dev_complete_response(struct gatt_device *device,
uint8_t opcode)
{
- uint8_t rsp[ATT_DEFAULT_LE_MTU];
+ size_t mtu;
+ uint8_t *rsp = g_attrib_get_buffer(device->attrib, &mtu);
struct pending_request *val;
uint16_t len = 0;
uint8_t error = 0;
+ if (queue_find(device->pending_requests, match_pending_dev_request,
+ NULL)) {
+ DBG("Still pending requests");
+ return;
+ }
+
switch (opcode) {
case ATT_OP_READ_BY_TYPE_REQ: {
struct att_data_list *adl;
@@ -3505,7 +4045,6 @@
int length;
struct queue *temp;
-
temp = queue_new();
if (!temp)
goto done;
@@ -3517,9 +4056,15 @@
goto done;
}
+ if (val->error) {
+ queue_destroy(temp, NULL);
+ error = val->error;
+ goto done;
+ }
+
length = val->length;
- while (val && val->length == length) {
+ while (val && val->length == length && val->error == 0) {
queue_push_tail(temp, val);
val = queue_pop_head(device->pending_requests);
}
@@ -3538,7 +4083,7 @@
val = queue_pop_head(temp);
}
- len = enc_read_by_type_resp(adl, rsp, sizeof(rsp));
+ len = enc_read_by_type_resp(adl, rsp, mtu);
att_data_list_free(adl);
queue_destroy(temp, destroy_pending_request);
@@ -3547,23 +4092,23 @@
}
case ATT_OP_READ_BLOB_REQ:
val = queue_pop_head(device->pending_requests);
- if (!val || val->length < 0) {
- error = ATT_ECODE_ATTR_NOT_FOUND;
+ if (val->error) {
+ error = val->error;
goto done;
}
len = enc_read_blob_resp(val->value, val->length, val->offset,
- rsp, sizeof(rsp));
+ rsp, mtu);
destroy_pending_request(val);
break;
case ATT_OP_READ_REQ:
val = queue_pop_head(device->pending_requests);
- if (!val || val->length < 0) {
- error = ATT_ECODE_ATTR_NOT_FOUND;
+ if (val->error) {
+ error = val->error;
goto done;
}
- len = enc_read_resp(val->value, val->length, rsp, sizeof(rsp));
+ len = enc_read_resp(val->value, val->length, rsp, mtu);
destroy_pending_request(val);
break;
case ATT_OP_READ_BY_GROUP_REQ: {
@@ -3609,7 +4154,7 @@
val = queue_pop_head(temp);
}
- len = enc_read_by_grp_resp(adl, rsp, sizeof(rsp));
+ len = enc_read_by_grp_resp(adl, rsp, mtu);
att_data_list_free(adl);
queue_destroy(temp, destroy_pending_request);
@@ -3657,7 +4202,7 @@
}
if (list && !error)
- len = enc_find_by_type_resp(list, rsp, sizeof(rsp));
+ len = enc_find_by_type_resp(list, rsp, mtu);
else
error = ATT_ECODE_ATTR_NOT_FOUND;
@@ -3667,9 +4212,9 @@
}
case ATT_OP_EXEC_WRITE_REQ:
val = queue_pop_head(device->pending_requests);
- if (!val) {
- error = ATT_ECODE_ATTR_NOT_FOUND;
- break;
+ if (val->error) {
+ error = val->error;
+ goto done;
}
len = enc_exec_write_resp(rsp);
@@ -3677,9 +4222,9 @@
break;
case ATT_OP_WRITE_REQ:
val = queue_pop_head(device->pending_requests);
- if (!val) {
- error = ATT_ECODE_ATTR_NOT_FOUND;
- break;
+ if (val->error) {
+ error = val->error;
+ goto done;
}
len = enc_write_resp(rsp);
@@ -3687,13 +4232,13 @@
break;
case ATT_OP_PREP_WRITE_REQ:
val = queue_pop_head(device->pending_requests);
- if (!val) {
- error = ATT_ECODE_ATTR_NOT_FOUND;
- break;
+ if (val->error) {
+ error = val->error;
+ goto done;
}
len = enc_prep_write_resp(val->handle, val->offset, val->value,
- val->length, rsp, sizeof(rsp));
+ val->length, rsp, mtu);
destroy_pending_request(val);
break;
default:
@@ -3702,8 +4247,7 @@
done:
if (!len)
- len = enc_error_resp(opcode, 0x0000, error, rsp,
- ATT_DEFAULT_LE_MTU);
+ len = enc_error_resp(opcode, 0x0000, error, rsp, mtu);
g_attrib_send(device->attrib, 0, rsp, len, NULL, NULL, NULL);
@@ -3716,13 +4260,6 @@
struct gatt_device *device;
};
-static bool match_pending_dev_request(const void *data, const void *user_data)
-{
- const struct pending_request *pending_request = data;
-
- return pending_request->length == READ_PENDING;
-}
-
static bool match_dev_request_by_handle(const void *data, const void *user_data)
{
const struct pending_request *handle_data = data;
@@ -3731,72 +4268,106 @@
return handle_data->handle == handle;
}
-static void read_requested_attributes(void *data, void *user_data)
+static uint8_t check_device_permissions(struct gatt_device *device,
+ uint8_t opcode, uint32_t permissions)
{
- struct pending_request *resp_data = data;
- struct request_processing_data *process_data = user_data;
- uint8_t *value;
- int value_len;
+ GIOChannel *io;
+ int sec_level;
- if (!gatt_db_read(gatt_db, resp_data->handle,
- resp_data->offset,
- process_data->opcode,
- &process_data->device->bdaddr,
- &value, &value_len)) {
- resp_data->length = READ_FAILED;
+ io = g_attrib_get_channel(device->attrib);
+
+ if (!bt_io_get(io, NULL, BT_IO_OPT_SEC_LEVEL, &sec_level,
+ BT_IO_OPT_INVALID))
+ return ATT_ECODE_UNLIKELY;
+
+ DBG("opcode %u permissions %u sec_level %u", opcode, permissions,
+ sec_level);
+
+ switch (opcode) {
+ case ATT_OP_SIGNED_WRITE_CMD:
+ if (!(permissions & GATT_PERM_WRITE_SIGNED))
+ return ATT_ECODE_WRITE_NOT_PERM;
+
+ if ((permissions & GATT_PERM_WRITE_SIGNED_MITM) &&
+ sec_level < BT_SECURITY_HIGH)
+ return ATT_ECODE_AUTHENTICATION;
+ break;
+ case ATT_OP_READ_BY_TYPE_REQ:
+ case ATT_OP_READ_REQ:
+ case ATT_OP_READ_BLOB_REQ:
+ case ATT_OP_READ_MULTI_REQ:
+ case ATT_OP_READ_BY_GROUP_REQ:
+ case ATT_OP_FIND_BY_TYPE_REQ:
+ case ATT_OP_FIND_INFO_REQ:
+ if (!(permissions & GATT_PERM_READ))
+ return ATT_ECODE_READ_NOT_PERM;
+
+ if ((permissions & GATT_PERM_READ_MITM) &&
+ sec_level < BT_SECURITY_HIGH)
+ return ATT_ECODE_AUTHENTICATION;
+
+ if ((permissions & GATT_PERM_READ_ENCRYPTED) &&
+ sec_level < BT_SECURITY_MEDIUM)
+ return ATT_ECODE_INSUFF_ENC;
+
+ if (permissions & GATT_PERM_READ_AUTHORIZATION)
+ return ATT_ECODE_AUTHORIZATION;
+ break;
+ case ATT_OP_WRITE_REQ:
+ case ATT_OP_WRITE_CMD:
+ case ATT_OP_PREP_WRITE_REQ:
+ case ATT_OP_EXEC_WRITE_REQ:
+ if (!(permissions & GATT_PERM_WRITE))
+ return ATT_ECODE_WRITE_NOT_PERM;
+
+ if ((permissions & GATT_PERM_WRITE_MITM) &&
+ sec_level < BT_SECURITY_HIGH)
+ return ATT_ECODE_AUTHENTICATION;
+
+ if ((permissions & GATT_PERM_WRITE_ENCRYPTED) &&
+ sec_level < BT_SECURITY_MEDIUM)
+ return ATT_ECODE_INSUFF_ENC;
+
+ if (permissions & GATT_PERM_WRITE_AUTHORIZATION)
+ return ATT_ECODE_AUTHORIZATION;
+ break;
+ default:
+ return ATT_ECODE_UNLIKELY;
+ }
+
+ return 0;
+}
+
+static void fill_gatt_response(struct pending_request *request, uint16_t handle,
+ uint16_t offset, uint8_t status,
+ uint16_t len, const uint8_t *data)
+{
+ request->handle = handle;
+ request->offset = offset;
+ request->length = len;
+ request->state = REQUEST_DONE;
+ request->error = status;
+
+ if (!len)
+ return;
+
+ request->value = malloc0(len);
+ if (!request->value) {
+ request->error = ATT_ECODE_INSUFF_RESOURCES;
+
return;
}
- /* We have value here already if no callback will be called */
- if (value_len >= 0) {
- resp_data->value = malloc0(value_len);
- if (!resp_data->value) {
- /* If data cannot be copied, act like when read fails */
- resp_data->length = READ_FAILED;
- return;
- }
-
- memcpy(resp_data->value, value, value_len);
- resp_data->length = value_len;
- } else if (resp_data->length == READ_INIT) {
- resp_data->length = READ_PENDING;
- }
+ memcpy(request->value, data, len);
}
-static void process_dev_pending_requests(struct gatt_device *device,
- uint8_t att_opcode)
+static void fill_gatt_response_by_handle(uint16_t handle, uint16_t offset,
+ uint8_t status, uint16_t len,
+ const uint8_t *data,
+ struct gatt_device *dev)
{
- struct request_processing_data process_data;
-
- process_data.device = device;
- process_data.opcode = att_opcode;
-
- /* Process pending requests and prepare response */
- queue_foreach(device->pending_requests, read_requested_attributes,
- &process_data);
-
- if (!queue_find(device->pending_requests,
- match_pending_dev_request, NULL))
- send_dev_pending_response(device, att_opcode);
-}
-
-static void send_gatt_response(uint8_t opcode, uint16_t handle,
- uint16_t offset, uint8_t status,
- uint16_t len, const uint8_t *data,
- bdaddr_t *bdaddr)
-{
- struct gatt_device *dev;
struct pending_request *entry;
- dev = find_device_by_addr(bdaddr);
- if (!dev) {
- error("gatt: send_gatt_response, could not find dev");
- goto done;
- }
-
- if (status)
- goto done;
-
entry = queue_find(dev->pending_requests, match_dev_request_by_handle,
UINT_TO_PTR(handle));
if (!entry) {
@@ -3804,49 +4375,104 @@
return;
}
- entry->handle = handle;
- entry->offset = offset;
- entry->length = len;
+ fill_gatt_response(entry, handle, offset, status, len, data);
+}
- if (!len)
+static void read_requested_attributes(void *data, void *user_data)
+{
+ struct pending_request *resp_data = data;
+ struct request_processing_data *process_data = user_data;
+ uint32_t permissions;
+ uint8_t *value, error;
+ int value_len = 0;
+
+ if (!gatt_db_get_attribute_permissions(gatt_db, resp_data->handle,
+ &permissions)) {
+ resp_data->error = ATT_ECODE_ATTR_NOT_FOUND;
+ resp_data->state = REQUEST_DONE;
+ return;
+ }
+
+ /*
+ * Check if it is attribute we didn't declare permissions, like service
+ * declaration or included service. Set permissions to read only
+ */
+ if (permissions == 0)
+ permissions = GATT_PERM_READ;
+
+ error = check_device_permissions(process_data->device,
+ process_data->opcode,
+ permissions);
+ if (error)
goto done;
- entry->value = malloc0(len);
- if (!entry->value) {
- /* send_dev_pending_request on empty queue sends error resp. */
- queue_remove(dev->pending_requests, entry);
- destroy_pending_request(entry);
+ resp_data->state = REQUEST_PENDING;
+ if (!gatt_db_read(gatt_db, resp_data->handle,
+ resp_data->offset,
+ process_data->opcode,
+ &process_data->device->bdaddr,
+ &value, &value_len)) {
+ error = ATT_ECODE_UNLIKELY;
goto done;
}
- memcpy(entry->value, data, len);
-
done:
- if (!queue_find(dev->pending_requests, match_pending_dev_request, NULL))
- send_dev_pending_response(dev, opcode);
+ /* We have value here already if no callback will be called */
+ if (value_len >= 0)
+ fill_gatt_response(resp_data, resp_data->handle,
+ resp_data->offset, error, value_len,
+ value);
}
-static void set_trans_id(struct gatt_app *app, unsigned int id, int8_t opcode)
+static void process_dev_pending_requests(struct gatt_device *device,
+ uint8_t att_opcode)
{
- app->trans_id.id = id;
- app->trans_id.opcode = opcode;
+ struct request_processing_data process_data;
+
+ if (queue_isempty(device->pending_requests))
+ return;
+
+ process_data.device = device;
+ process_data.opcode = att_opcode;
+
+ /* Process pending requests and prepare response */
+ queue_foreach(device->pending_requests, read_requested_attributes,
+ &process_data);
+
+ send_dev_complete_response(device, att_opcode);
}
-static void clear_trans_id(struct gatt_app *app)
+static struct pending_trans_data *conn_add_transact(struct app_connection *conn,
+ uint8_t opcode)
{
- app->trans_id.id = 0;
- app->trans_id.opcode = 0;
+ struct pending_trans_data *transaction;
+ static int32_t trans_id = 1;
+
+ transaction = new0(struct pending_trans_data, 1);
+ if (!transaction)
+ return NULL;
+
+ if (!queue_push_tail(conn->transactions, transaction)) {
+ free(transaction);
+ return NULL;
+ }
+
+ transaction->id = trans_id++;
+ transaction->opcode = opcode;
+
+ return transaction;
}
static void read_cb(uint16_t handle, uint16_t offset, uint8_t att_opcode,
bdaddr_t *bdaddr, void *user_data)
{
+ struct pending_trans_data *transaction;
struct hal_ev_gatt_server_request_read ev;
struct gatt_app *app;
struct app_connection *conn;
int32_t id = PTR_TO_INT(user_data);
- static int32_t trans_id = 1;
+ struct gatt_device *dev;
app = find_app_by_id(id);
if (!app) {
@@ -3863,14 +4489,16 @@
memset(&ev, 0, sizeof(ev));
/* Store the request data, complete callback and transaction id */
- set_trans_id(app, trans_id++, att_opcode);
+ transaction = conn_add_transact(conn, att_opcode);
+ if (!transaction)
+ goto failed;
bdaddr2android(bdaddr, ev.bdaddr);
ev.conn_id = conn->id;
ev.attr_handle = handle;
ev.offset = offset;
ev.is_long = att_opcode == ATT_OP_READ_BLOB_REQ;
- ev.trans_id = app->trans_id.id;
+ ev.trans_id = transaction->id;
ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
HAL_EV_GATT_SERVER_REQUEST_READ,
@@ -3879,8 +4507,10 @@
return;
failed:
- send_gatt_response(att_opcode, handle, 0, ATT_ECODE_UNLIKELY, 0,
- NULL, bdaddr);
+ dev = find_device_by_addr(bdaddr);
+ if (dev)
+ fill_gatt_response_by_handle(handle, 0, ATT_ECODE_UNLIKELY, 0,
+ NULL, dev);
}
static void write_cb(uint16_t handle, uint16_t offset,
@@ -3888,11 +4518,13 @@
uint8_t att_opcode, bdaddr_t *bdaddr,
void *user_data)
{
- struct hal_ev_gatt_server_request_write ev;
+ uint8_t buf[IPC_MTU];
+ struct hal_ev_gatt_server_request_write *ev = (void *) buf;
+ struct pending_trans_data *transaction;
struct gatt_app *app;
int32_t id = PTR_TO_INT(user_data);
- static int32_t trans_id = 1;
struct app_connection *conn;
+ struct gatt_device *dev;
app = find_app_by_id(id);
if (!app) {
@@ -3906,33 +4538,77 @@
goto failed;
}
- /* Store the request data, complete callback and transaction id */
- set_trans_id(app, trans_id++, att_opcode);
+ /*
+ * Remember that this application has ongoing prep write
+ * Need it later to find out where to send execute write
+ */
+ if (att_opcode == ATT_OP_PREP_WRITE_REQ)
+ conn->wait_execute_write = true;
- /* TODO figure it out */
- if (att_opcode == ATT_OP_EXEC_WRITE_REQ)
+ /* Store the request data, complete callback and transaction id */
+ transaction = conn_add_transact(conn, att_opcode);
+ if (!transaction)
goto failed;
- memset(&ev, 0, sizeof(ev));
+ memset(ev, 0, sizeof(*ev));
- bdaddr2android(bdaddr, ev.bdaddr);
- ev.attr_handle = handle;
- ev.offset = offset;
+ bdaddr2android(bdaddr, &ev->bdaddr);
+ ev->attr_handle = handle;
+ ev->offset = offset;
- ev.conn_id = conn->id;
- ev.trans_id = app->trans_id.id;
+ ev->conn_id = conn->id;
+ ev->trans_id = transaction->id;
- ev.is_prep = att_opcode == ATT_OP_PREP_WRITE_REQ;
- ev.need_rsp = att_opcode == ATT_OP_WRITE_REQ;
+ ev->is_prep = att_opcode == ATT_OP_PREP_WRITE_REQ;
+ ev->need_rsp = att_opcode == ATT_OP_WRITE_REQ;
- ev.length = len;
- memcpy(&ev.value, value, len);
+ ev->length = len;
+ memcpy(ev->value, value, len);
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_EV_GATT_SERVER_REQUEST_WRITE,
+ sizeof(*ev) + ev->length , ev);
return;
failed:
- send_gatt_response(att_opcode, handle, 0, ATT_ECODE_UNLIKELY, 0, NULL,
- bdaddr);
+ dev = find_device_by_addr(bdaddr);
+ if (dev)
+ fill_gatt_response_by_handle(handle, 0, ATT_ECODE_UNLIKELY, 0,
+ NULL, dev);
+}
+
+static uint32_t android_to_gatt_permissions(int32_t hal_permissions)
+{
+ uint32_t permissions = 0;
+
+ if (hal_permissions & HAL_GATT_PERMISSION_READ)
+ permissions |= GATT_PERM_READ;
+
+ if (hal_permissions & HAL_GATT_PERMISSION_READ_ENCRYPTED)
+ permissions |= GATT_PERM_READ_ENCRYPTED | GATT_PERM_READ;
+
+ if (hal_permissions & HAL_GATT_PERMISSION_READ_ENCRYPTED_MITM)
+ permissions |= GATT_PERM_READ_MITM | GATT_PERM_READ_ENCRYPTED |
+ GATT_PERM_READ;
+
+ if (hal_permissions & HAL_GATT_PERMISSION_WRITE)
+ permissions |= GATT_PERM_WRITE;
+
+ if (hal_permissions & HAL_GATT_PERMISSION_WRITE_ENCRYPTED)
+ permissions |= GATT_PERM_WRITE_ENCRYPTED | GATT_PERM_WRITE;
+
+ if (hal_permissions & HAL_GATT_PERMISSION_WRITE_ENCRYPTED_MITM)
+ permissions |= GATT_PERM_WRITE_MITM |
+ GATT_PERM_WRITE_ENCRYPTED | GATT_PERM_WRITE;
+
+ if (hal_permissions & HAL_GATT_PERMISSION_WRITE_SIGNED)
+ permissions |= GATT_PERM_WRITE_SIGNED;
+
+ if (hal_permissions & HAL_GATT_PERMISSION_WRITE_SIGNED_MITM)
+ permissions |= GATT_PERM_WRITE_SIGNED_MITM |
+ GATT_PERM_WRITE_SIGNED;
+
+ return permissions;
}
static void handle_server_add_characteristic(const void *buf, uint16_t len)
@@ -3942,6 +4618,7 @@
struct gatt_app *server;
bt_uuid_t uuid;
uint8_t status;
+ uint32_t permissions;
int32_t app_id = cmd->server_if;
DBG("");
@@ -3955,11 +4632,11 @@
}
android2uuid(cmd->uuid, &uuid);
+ permissions = android_to_gatt_permissions(cmd->permissions);
- /*FIXME: Handle properties. Register callback if needed. */
ev.char_handle = gatt_db_add_characteristic(gatt_db,
cmd->service_handle,
- &uuid, cmd->permissions,
+ &uuid, permissions,
cmd->properties,
read_cb, write_cb,
INT_TO_PTR(app_id));
@@ -3989,6 +4666,7 @@
struct gatt_app *server;
bt_uuid_t uuid;
uint8_t status;
+ uint32_t permissions;
int32_t app_id = cmd->server_if;
DBG("");
@@ -4002,11 +4680,11 @@
}
android2uuid(cmd->uuid, &uuid);
+ permissions = android_to_gatt_permissions(cmd->permissions);
- /*FIXME: Handle properties. Register callback if needed. */
ev.descr_handle = gatt_db_add_char_descriptor(gatt_db,
cmd->service_handle,
- &uuid, cmd->permissions,
+ &uuid, permissions,
read_cb, write_cb,
INT_TO_PTR(app_id));
if (!ev.descr_handle)
@@ -4027,6 +4705,20 @@
HAL_OP_GATT_SERVER_ADD_DESCRIPTOR, status);
}
+static void notify_service_change(void *data, void *user_data)
+{
+ struct att_range range;
+
+ range.start = PTR_TO_UINT(user_data);
+ range.end = gatt_db_get_end_handle(gatt_db, range.start);
+
+ /* In case of db error */
+ if (!range.end)
+ return;
+
+ notify_att_range_change(data, &range);
+}
+
static void handle_server_start_service(const void *buf, uint16_t len)
{
const struct hal_cmd_gatt_server_start_service *cmd = buf;
@@ -4052,6 +4744,9 @@
goto failed;
}
+ queue_foreach(gatt_devices, notify_service_change,
+ UINT_TO_PTR(cmd->service_handle));
+
status = HAL_STATUS_SUCCESS;
failed:
@@ -4088,6 +4783,9 @@
else
status = HAL_STATUS_SUCCESS;
+ queue_foreach(gatt_devices, notify_service_change,
+ UINT_TO_PTR(cmd->service_handle));
+
failed:
ev.status = status == HAL_STATUS_SUCCESS ? GATT_SUCCESS : GATT_FAILURE;
ev.server_if = cmd->server_if;
@@ -4139,10 +4837,11 @@
static void handle_server_send_indication(const void *buf, uint16_t len)
{
const struct hal_cmd_gatt_server_send_indication *cmd = buf;
- uint8_t pdu[ATT_DEFAULT_LE_MTU];
struct app_connection *conn;
uint8_t status;
uint16_t length;
+ uint8_t *pdu;
+ size_t mtu;
DBG("");
@@ -4153,15 +4852,17 @@
goto reply;
}
+ pdu = g_attrib_get_buffer(conn->device->attrib, &mtu);
+
if (cmd->confirm)
/* TODO: Add data to track confirmation for this request */
length = enc_indication(cmd->attribute_handle,
- (uint8_t *)cmd->value, cmd->len,
- pdu, sizeof(pdu));
+ (uint8_t *)cmd->value, cmd->len, pdu,
+ mtu);
else
length = enc_notification(cmd->attribute_handle,
(uint8_t *)cmd->value, cmd->len,
- pdu, sizeof(pdu));
+ pdu, mtu);
g_attrib_send(conn->device->attrib, 0, pdu, length, NULL, NULL, NULL);
@@ -4172,11 +4873,33 @@
HAL_OP_GATT_SERVER_SEND_INDICATION, status);
}
+static bool match_trans_id(const void *data, const void *user_data)
+{
+ const struct pending_trans_data *transaction = data;
+
+ return transaction->id == PTR_TO_UINT(user_data);
+}
+
+
+static bool find_conn_waiting_exec_write(const void *data,
+ const void *user_data)
+{
+ const struct app_connection *conn = data;
+
+ return conn->wait_execute_write;
+}
+
+static bool pending_execute_write(void)
+{
+ return queue_find(app_connections, find_conn_waiting_exec_write, NULL);
+}
+
static void handle_server_send_response(const void *buf, uint16_t len)
{
const struct hal_cmd_gatt_server_send_response *cmd = buf;
+ struct pending_trans_data *transaction;
+ uint16_t handle = cmd->handle;
struct app_connection *conn;
- struct gatt_app *app;
uint8_t status;
DBG("");
@@ -4188,22 +4911,37 @@
goto reply;
}
- app = conn->app;
-
- if ((unsigned int)cmd->trans_id != app->trans_id.id) {
- error("gatt: transaction ID mismatch (%d!=%d)",
- cmd->trans_id, app->trans_id.id);
-
+ transaction = queue_remove_if(conn->transactions, match_trans_id,
+ UINT_TO_PTR(cmd->trans_id));
+ if (!transaction) {
+ error("gatt: transaction ID = %d not found", cmd->trans_id);
status = HAL_STATUS_FAILED;
goto reply;
}
- send_gatt_response(conn->app->trans_id.opcode, cmd->handle, cmd->offset,
- cmd->status, cmd->len, cmd->data,
- &conn->device->bdaddr);
+ if (transaction->opcode == ATT_OP_EXEC_WRITE_REQ) {
+ conn->wait_execute_write = false;
+ /* Check for execute response from all server applications */
+ if (pending_execute_write())
+ goto done;
+
+ /* Make sure handle is 0. We need it to find pending request */
+ handle = 0;
+
+ /*
+ * FIXME: Handle situation when not all server applications
+ * respond with a success.
+ */
+ }
+
+ fill_gatt_response_by_handle(handle, cmd->offset, cmd->status, cmd->len,
+ cmd->data, conn->device);
+ send_dev_complete_response(conn->device, transaction->opcode);
+
+done:
/* Clean request data */
- clear_trans_id(app);
+ free(transaction);
status = HAL_STATUS_SUCCESS;
@@ -4321,8 +5059,6 @@
};
static uint8_t read_by_group_type(const uint8_t *cmd, uint16_t cmd_len,
- uint8_t *rsp, size_t rsp_size,
- uint16_t *length,
struct gatt_device *device)
{
uint16_t start, end;
@@ -4334,6 +5070,9 @@
if (!len)
return ATT_ECODE_INVALID_PDU;
+ if (start > end || start == 0)
+ return ATT_ECODE_INVALID_HANDLE;
+
q = queue_new();
if (!q)
return ATT_ECODE_INSUFF_RESOURCES;
@@ -4347,8 +5086,6 @@
while (queue_peek_head(q)) {
uint16_t handle = PTR_TO_UINT(queue_pop_head(q));
- uint8_t *value;
- int value_len;
struct pending_request *entry;
entry = new0(struct pending_request, 1);
@@ -4358,31 +5095,19 @@
}
entry->handle = handle;
-
- if (!gatt_db_read(gatt_db, handle, 0, ATT_OP_READ_BY_GROUP_REQ,
- &device->bdaddr, &value, &value_len))
- break;
-
- entry->value = malloc0(value_len);
- if (!entry->value) {
- queue_destroy(q, destroy_pending_request);
- return ATT_ECODE_UNLIKELY;
- }
-
- memcpy(entry->value, value, value_len);
- entry->length = value_len;
+ entry->state = REQUEST_INIT;
if (!queue_push_tail(device->pending_requests, entry)) {
queue_remove_all(device->pending_requests, NULL, NULL,
destroy_pending_request);
+ free(entry);
queue_destroy(q, NULL);
return ATT_ECODE_UNLIKELY;
}
}
queue_destroy(q, NULL);
-
- send_dev_pending_response(device, cmd[0]);
+ process_dev_pending_requests(device, cmd[0]);
return 0;
}
@@ -4401,6 +5126,9 @@
if (!len)
return ATT_ECODE_INVALID_PDU;
+ if (start > end || start == 0)
+ return ATT_ECODE_INVALID_HANDLE;
+
q = queue_new();
if (!q)
return ATT_ECODE_INSUFF_RESOURCES;
@@ -4422,7 +5150,7 @@
return ATT_ECODE_INSUFF_RESOURCES;
}
- data->length = READ_INIT;
+ data->state = REQUEST_INIT;
data->handle = handle;
queue_push_tail(device->pending_requests, data);
}
@@ -4461,13 +5189,16 @@
return ATT_ECODE_REQ_NOT_SUPP;
}
+ if (handle == 0)
+ return ATT_ECODE_INVALID_HANDLE;
+
data = new0(struct pending_request, 1);
if (!data)
return ATT_ECODE_INSUFF_RESOURCES;
data->offset = offset;
data->handle = handle;
- data->length = READ_INIT;
+ data->state = REQUEST_INIT;
if (!queue_push_tail(dev->pending_requests, data)) {
free(data);
return ATT_ECODE_INSUFF_RESOURCES;
@@ -4479,14 +5210,14 @@
}
static uint8_t mtu_att_handle(const uint8_t *cmd, uint16_t cmd_len,
- uint8_t *rsp, size_t rsp_size,
- struct gatt_device *dev,
- uint16_t *length)
+ struct gatt_device *dev)
{
uint16_t mtu, imtu, omtu;
+ size_t length;
GIOChannel *io;
GError *gerr = NULL;
uint16_t len;
+ uint8_t *rsp;
DBG("");
@@ -4509,16 +5240,18 @@
return ATT_ECODE_UNLIKELY;
}
- /* Limit OMTU to received value */
- mtu = MIN(mtu, omtu);
- g_attrib_set_mtu(dev->attrib, mtu);
+ rsp = g_attrib_get_buffer(dev->attrib, &length);
/* Respond with our IMTU */
- len = enc_mtu_resp(imtu, rsp, rsp_size);
+ len = enc_mtu_resp(imtu, rsp, length);
if (!len)
return ATT_ECODE_UNLIKELY;
- *length = len;
+ g_attrib_send(dev->attrib, 0, rsp, len, NULL, NULL, NULL);
+
+ /* Limit OMTU to received value */
+ mtu = MIN(mtu, omtu);
+ g_attrib_set_mtu(dev->attrib, mtu);
return 0;
}
@@ -4539,6 +5272,9 @@
if (!len)
return ATT_ECODE_INVALID_PDU;
+ if (start > end || start == 0)
+ return ATT_ECODE_INVALID_HANDLE;
+
q = queue_new();
if (!q)
return ATT_ECODE_UNLIKELY;
@@ -4593,7 +5329,7 @@
static uint8_t find_by_type_request(const uint8_t *cmd, uint16_t cmd_len,
struct gatt_device *device)
{
- uint8_t search_value[ATT_DEFAULT_LE_MTU];
+ uint8_t search_value[cmd_len];
size_t search_vlen;
uint16_t start, end;
uint16_t handle;
@@ -4608,6 +5344,9 @@
if (!len)
return ATT_ECODE_INVALID_PDU;
+ if (start > end || start == 0)
+ return ATT_ECODE_INVALID_HANDLE;
+
q = queue_new();
if (!q)
return ATT_ECODE_UNLIKELY;
@@ -4625,13 +5364,13 @@
}
data->filter_value = malloc0(search_vlen);
- if (!data) {
+ if (!data->filter_value) {
destroy_pending_request(data);
queue_destroy(q, NULL);
return ATT_ECODE_INSUFF_RESOURCES;
}
- data->length = READ_INIT;
+ data->state = REQUEST_INIT;
data->handle = handle;
data->filter_vlen = search_vlen;
memcpy(data->filter_value, search_value, search_vlen);
@@ -4648,40 +5387,139 @@
return 0;
}
-static uint8_t write_cmd_request(const uint8_t *cmd, uint16_t cmd_len,
+static void write_cmd_request(const uint8_t *cmd, uint16_t cmd_len,
struct gatt_device *dev)
{
- uint8_t value[ATT_DEFAULT_LE_MTU];
+ uint8_t value[cmd_len];
+ uint32_t permissions;
uint16_t handle;
uint16_t len;
size_t vlen;
len = dec_write_cmd(cmd, cmd_len, &handle, value, &vlen);
if (!len)
- return ATT_ECODE_INVALID_PDU;
+ return;
- if (!gatt_db_write(gatt_db, handle, 0, value, vlen, cmd[0],
- &dev->bdaddr))
- return ATT_ECODE_UNLIKELY;
+ if (handle == 0)
+ return;
- return 0;
+ if (!gatt_db_get_attribute_permissions(gatt_db, handle, &permissions))
+ return;
+
+ if (check_device_permissions(dev, cmd[0], permissions))
+ return;
+
+ gatt_db_write(gatt_db, handle, 0, value, vlen, cmd[0], &dev->bdaddr);
+}
+
+static void write_signed_cmd_request(const uint8_t *cmd, uint16_t cmd_len,
+ struct gatt_device *dev)
+{
+ uint8_t value[ATT_DEFAULT_LE_MTU];
+ uint8_t s[ATT_SIGNATURE_LEN];
+ uint32_t permissions;
+ uint16_t handle;
+ uint16_t len;
+ size_t vlen;
+ uint8_t csrk[16];
+ uint32_t sign_cnt;
+
+ if (get_sec_level(dev) != BT_SECURITY_LOW) {
+ error("gatt: Remote tries write signed on encrypted link");
+ connection_cleanup(dev);
+ return;
+ }
+
+ if (!bt_get_csrk(&dev->bdaddr, REMOTE_CSRK, csrk, &sign_cnt)) {
+ error("gatt: No valid csrk from remote device");
+ return;
+ }
+
+ len = dec_signed_write_cmd(cmd, cmd_len, &handle, value, &vlen, s);
+
+ if (handle == 0)
+ return;
+
+ if (!gatt_db_get_attribute_permissions(gatt_db, handle, &permissions))
+ return;
+
+ if (check_device_permissions(dev, cmd[0], permissions))
+ return;
+
+ if (len) {
+ uint8_t t[ATT_SIGNATURE_LEN];
+ uint32_t r_sign_cnt = get_le32(s);
+
+ if (r_sign_cnt != sign_cnt) {
+ error("gatt: sign_cnt does not match (%d!=%d)",
+ sign_cnt, r_sign_cnt);
+ return;
+ }
+
+ /* Generate signature and verify it */
+ if (!bt_crypto_sign_att(crypto, csrk, cmd,
+ cmd_len - ATT_SIGNATURE_LEN,
+ sign_cnt, t)) {
+ error("gatt: Error when generating att signature");
+ return;
+ }
+
+ if (memcmp(t, s, ATT_SIGNATURE_LEN)) {
+ error("gatt: signature does not match");
+ return;
+ }
+ /* Signature OK, proceed with write */
+ bt_update_sign_counter(&dev->bdaddr, REMOTE_CSRK);
+ gatt_db_write(gatt_db, handle, 0, value, vlen, cmd[0],
+ &dev->bdaddr);
+ }
}
static uint8_t write_req_request(const uint8_t *cmd, uint16_t cmd_len,
struct gatt_device *dev)
{
- uint8_t value[ATT_DEFAULT_LE_MTU];
+ uint8_t value[cmd_len];
+ struct pending_request *data;
+ uint32_t permissions;
uint16_t handle;
uint16_t len;
+ uint8_t error;
size_t vlen;
len = dec_write_req(cmd, cmd_len, &handle, value, &vlen);
if (!len)
return ATT_ECODE_INVALID_PDU;
+ if (handle == 0)
+ return ATT_ECODE_INVALID_HANDLE;
+
+ if (!gatt_db_get_attribute_permissions(gatt_db, handle, &permissions))
+ return ATT_ECODE_ATTR_NOT_FOUND;
+
+ error = check_device_permissions(dev, cmd[0], permissions);
+ if (error)
+ return error;
+
+ data = new0(struct pending_request, 1);
+ if (!data)
+ return ATT_ECODE_INSUFF_RESOURCES;
+
+ data->handle = handle;
+ data->state = REQUEST_PENDING;
+
+ if (!queue_push_tail(dev->pending_requests, data)) {
+ free(data);
+ return ATT_ECODE_INSUFF_RESOURCES;
+ }
+
if (!gatt_db_write(gatt_db, handle, 0, value, vlen, cmd[0],
- &dev->bdaddr))
+ &dev->bdaddr)) {
+ queue_remove(dev->pending_requests, data);
+ free(data);
return ATT_ECODE_UNLIKELY;
+ }
+
+ send_dev_complete_response(dev, cmd[0]);
return 0;
}
@@ -4689,9 +5527,12 @@
static uint8_t write_prep_request(const uint8_t *cmd, uint16_t cmd_len,
struct gatt_device *dev)
{
- uint8_t value[ATT_DEFAULT_LE_MTU];
+ uint8_t value[cmd_len];
+ struct pending_request *data;
+ uint32_t permissions;
uint16_t handle;
uint16_t offset;
+ uint8_t error;
uint16_t len;
size_t vlen;
@@ -4700,6 +5541,29 @@
if (!len)
return ATT_ECODE_INVALID_PDU;
+ if (handle == 0)
+ return ATT_ECODE_INVALID_HANDLE;
+
+ if (!gatt_db_get_attribute_permissions(gatt_db, handle, &permissions))
+ return ATT_ECODE_ATTR_NOT_FOUND;
+
+ error = check_device_permissions(dev, cmd[0], permissions);
+ if (error)
+ return error;
+
+ data = new0(struct pending_request, 1);
+ if (!data)
+ return ATT_ECODE_INSUFF_RESOURCES;
+
+ data->handle = handle;
+ data->offset = offset;
+ data->state = REQUEST_PENDING;
+
+ if (!queue_push_tail(dev->pending_requests, data)) {
+ free(data);
+ return ATT_ECODE_INSUFF_RESOURCES;
+ }
+
if (!gatt_db_write(gatt_db, handle, offset, value, vlen, cmd[0],
&dev->bdaddr))
return ATT_ECODE_UNLIKELY;
@@ -4707,27 +5571,85 @@
return 0;
}
+static void send_server_write_execute_notify(void *data, void *user_data)
+{
+ struct hal_ev_gatt_server_request_exec_write *ev = user_data;
+ struct pending_trans_data *transaction;
+ struct app_connection *conn = data;
+
+ if (!conn->wait_execute_write)
+ return;
+
+ ev->conn_id = conn->id;
+
+ transaction = conn_add_transact(conn, ATT_OP_EXEC_WRITE_REQ);
+ if (!transaction) {
+ conn->wait_execute_write = false;
+ return;
+ }
+
+ ev->trans_id = transaction->id;
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_EV_GATT_SERVER_REQUEST_EXEC_WRITE,
+ sizeof(*ev), ev);
+}
+
+static uint8_t write_execute_request(const uint8_t *cmd, uint16_t cmd_len,
+ struct gatt_device *dev)
+{
+ struct hal_ev_gatt_server_request_exec_write ev;
+ uint8_t value;
+ struct pending_request *data;
+
+ /*
+ * Check if there was any write prep before.
+ * TODO: Try to find better error code if possible
+ */
+ if (!pending_execute_write())
+ return ATT_ECODE_UNLIKELY;
+
+ if (!dec_exec_write_req(cmd, cmd_len, &value))
+ return ATT_ECODE_INVALID_PDU;
+
+ memset(&ev, 0, sizeof(ev));
+ bdaddr2android(&dev->bdaddr, &ev.bdaddr);
+ ev.exec_write = value;
+
+ data = new0(struct pending_request, 1);
+ if (!data)
+ return ATT_ECODE_INSUFF_RESOURCES;
+
+ data->state = REQUEST_PENDING;
+ if (!queue_push_tail(dev->pending_requests, data)) {
+ free(data);
+ return ATT_ECODE_INSUFF_RESOURCES;
+ }
+
+ queue_foreach(app_connections, send_server_write_execute_notify, &ev);
+
+ return 0;
+}
+
static void att_handler(const uint8_t *ipdu, uint16_t len, gpointer user_data)
{
struct gatt_device *dev = user_data;
- uint8_t opdu[ATT_DEFAULT_LE_MTU];
uint8_t status;
- uint16_t length = 0;
- size_t vlen;
- uint8_t *value = g_attrib_get_buffer(dev->attrib, &vlen);
+ uint16_t resp_length = 0;
+ size_t length;
+ uint8_t *opdu = g_attrib_get_buffer(dev->attrib, &length);
DBG("op 0x%02x", ipdu[0]);
- if (len > vlen) {
- error("gatt: Too much data on ATT socket %p", value);
+ if (len > length) {
+ error("gatt: Too much data on ATT socket %p", opdu);
status = ATT_ECODE_INVALID_PDU;
goto done;
}
switch (ipdu[0]) {
case ATT_OP_READ_BY_GROUP_REQ:
- status = read_by_group_type(ipdu, len, opdu, sizeof(opdu),
- &length, dev);
+ status = read_by_group_type(ipdu, len, dev);
break;
case ATT_OP_READ_BY_TYPE_REQ:
status = read_by_type(ipdu, len, dev);
@@ -4737,12 +5659,11 @@
status = read_request(ipdu, len, dev);
break;
case ATT_OP_MTU_REQ:
- status = mtu_att_handle(ipdu, len, opdu, sizeof(opdu), dev,
- &length);
+ status = mtu_att_handle(ipdu, len, dev);
break;
case ATT_OP_FIND_INFO_REQ:
- status = find_info_handle(ipdu, len, opdu, sizeof(opdu),
- &length);
+ status = find_info_handle(ipdu, len, opdu, length,
+ &resp_length);
break;
case ATT_OP_WRITE_REQ:
status = write_req_request(ipdu, len, dev);
@@ -4750,10 +5671,13 @@
return;
break;
case ATT_OP_WRITE_CMD:
- status = write_cmd_request(ipdu, len, dev);
- if (!status)
- return;
- break;
+ write_cmd_request(ipdu, len, dev);
+ /* No response on write cmd */
+ return;
+ case ATT_OP_SIGNED_WRITE_CMD:
+ write_signed_cmd_request(ipdu, len, dev);
+ /* No response on write signed cmd */
+ return;
case ATT_OP_PREP_WRITE_REQ:
status = write_prep_request(ipdu, len, dev);
if (!status)
@@ -4762,11 +5686,24 @@
case ATT_OP_FIND_BY_TYPE_REQ:
status = find_by_type_request(ipdu, len, dev);
break;
- case ATT_OP_EXEC_WRITE_REQ:
- /* TODO */
- case ATT_OP_HANDLE_CNF:
case ATT_OP_HANDLE_IND:
+ /*
+ * We have to send confirmation here. If some client is
+ * registered for this indication, event will be send in
+ * handle_notification
+ */
+ resp_length = enc_confirmation(opdu, sizeof(opdu));
+ status = 0;
+ break;
case ATT_OP_HANDLE_NOTIFY:
+ /* Client will handle this */
+ return;
+ case ATT_OP_EXEC_WRITE_REQ:
+ status = write_execute_request(ipdu, len, dev);
+ if (!status)
+ return;
+ break;
+ case ATT_OP_HANDLE_CNF:
case ATT_OP_READ_MULTI_REQ:
default:
DBG("Unsupported request 0x%02x", ipdu[0]);
@@ -4776,11 +5713,12 @@
done:
if (status)
- length = enc_error_resp(ipdu[0], 0x0000, status, opdu,
- ATT_DEFAULT_LE_MTU);
+ resp_length = enc_error_resp(ipdu[0], 0x0000, status, opdu,
+ length);
- if (length)
- g_attrib_send(dev->attrib, 0, opdu, length, NULL, NULL, NULL);
+ if (resp_length)
+ g_attrib_send(dev->attrib, 0, opdu, resp_length, NULL, NULL,
+ NULL);
}
static void create_listen_connections(void *data, void *user_data)
@@ -4794,21 +5732,15 @@
create_connection(dev, app);
}
-static void connect_event(GIOChannel *io, GError *gerr, void *user_data)
+static void connect_confirm(GIOChannel *io, void *user_data)
{
struct gatt_device *dev;
uint8_t dst_type;
bdaddr_t dst;
- struct connect_data data;
+ GError *gerr = NULL;
DBG("");
- if (gerr) {
- error("gatt: %s", gerr->message);
- g_error_free(gerr);
- return;
- }
-
bt_io_get(io, &gerr,
BT_IO_OPT_DEST_BDADDR, &dst,
BT_IO_OPT_DEST_TYPE, &dst_type,
@@ -4825,7 +5757,7 @@
dev = create_device(&dst);
if (!dev) {
error("gatt: Could not create device");
- return;
+ goto drop;
}
dev->bdaddr_type = dst_type;
@@ -4836,32 +5768,23 @@
ba2str(&dst, addr);
info("gatt: Rejecting incoming connection from %s",
addr);
- return;
+ goto drop;
}
}
- dev->attrib = g_attrib_new(io);
- if (!dev->attrib) {
- error("gatt: unable to create new GAttrib instance");
- destroy_device(dev);
- return;
+ if (!bt_io_accept(io, connect_cb, device_ref(dev), NULL, NULL)) {
+ error("gatt: failed to accept connection");
+ device_unref(dev);
+ goto drop;
}
- dev->watch_id = g_io_add_watch(io, G_IO_HUP | G_IO_ERR | G_IO_NVAL,
- disconnected_cb, dev);
queue_foreach(listen_apps, create_listen_connections, dev);
+ device_set_state(dev, DEVICE_CONNECT_READY);
- data.dev = dev;
- data.status = GATT_SUCCESS;
- device_set_state(dev, DEVICE_CONNECTED);
+ return;
- queue_foreach(app_connections, send_app_connect_notifications, &data);
-
- dev->server_id = g_attrib_register(dev->attrib, GATTRIB_ALL_REQS,
- GATTRIB_ALL_HANDLES,
- att_handler, dev, NULL);
- if (dev->server_id == 0)
- error("gatt: Could not attach to server");
+drop:
+ g_io_channel_shutdown(io, TRUE, NULL);
}
struct gap_srvc_handles {
@@ -4902,9 +5825,8 @@
entry->value = malloc0(strlen(name));
if (!entry->value) {
- queue_remove(dev->pending_requests, entry);
- free(entry);
- return;
+ entry->error = ATT_ECODE_INSUFF_RESOURCES;
+ goto done;
}
entry->length = strlen(name);
@@ -4912,9 +5834,8 @@
} else if (handle == gap_srvc_data.appear) {
entry->value = malloc0(2);
if (!entry->value) {
- queue_remove(dev->pending_requests, entry);
- free(entry);
- return;
+ entry->error = ATT_ECODE_INSUFF_RESOURCES;
+ goto done;
}
put_le16(APPEARANCE_GENERIC_PHONE, entry->value);
@@ -4922,16 +5843,20 @@
} else if (handle == gap_srvc_data.priv) {
entry->value = malloc0(1);
if (!entry->value) {
- queue_remove(dev->pending_requests, entry);
- free(entry);
- return;
+ entry->error = ATT_ECODE_INSUFF_RESOURCES;
+ goto done;
}
*entry->value = PERIPHERAL_PRIVACY_DISABLE;
entry->length = sizeof(uint8_t);
+ } else {
+ entry->error = ATT_ECODE_ATTR_NOT_FOUND;
}
entry->offset = offset;
+
+done:
+ entry->state = REQUEST_DONE;
}
static void register_gap_service(void)
@@ -4946,7 +5871,7 @@
bt_uuid16_create(&uuid, GATT_CHARAC_DEVICE_NAME);
gap_srvc_data.dev_name =
gatt_db_add_characteristic(gatt_db, gap_srvc_data.srvc,
- &uuid, 0,
+ &uuid, GATT_PERM_READ,
GATT_CHR_PROP_READ,
gap_read_cb, NULL,
NULL);
@@ -4955,7 +5880,7 @@
bt_uuid16_create(&uuid, GATT_CHARAC_APPEARANCE);
gap_srvc_data.appear =
gatt_db_add_characteristic(gatt_db, gap_srvc_data.srvc,
- &uuid, 0,
+ &uuid, GATT_PERM_READ,
GATT_CHR_PROP_READ,
gap_read_cb, NULL,
NULL);
@@ -4964,7 +5889,7 @@
bt_uuid16_create(&uuid, GATT_CHARAC_PERIPHERAL_PRIV_FLAG);
gap_srvc_data.priv =
gatt_db_add_characteristic(gatt_db, gap_srvc_data.srvc,
- &uuid, 0,
+ &uuid, GATT_PERM_READ,
GATT_CHR_PROP_READ,
gap_read_cb, NULL,
NULL);
@@ -5012,14 +5937,16 @@
entry->value = malloc0(strlen(buf));
if (!entry->value) {
- queue_remove(dev->pending_requests, entry);
- free(entry);
- return;
+ entry->error = ATT_ECODE_UNLIKELY;
+ goto done;
}
entry->length = strlen(buf);
memcpy(entry->value, buf, entry->length);
entry->offset = offset;
+
+done:
+ entry->state = REQUEST_DONE;
}
static void register_device_info_service(void)
@@ -5035,43 +5962,43 @@
/* User data are not const hence (void *) cast is used */
bt_uuid16_create(&uuid, GATT_CHARAC_SYSTEM_ID);
- gatt_db_add_characteristic(gatt_db, srvc_handle, &uuid, 0,
+ gatt_db_add_characteristic(gatt_db, srvc_handle, &uuid, GATT_PERM_READ,
GATT_CHR_PROP_READ,
device_info_read_cb, NULL,
(void *) device_info.system_id);
bt_uuid16_create(&uuid, GATT_CHARAC_MODEL_NUMBER_STRING);
- gatt_db_add_characteristic(gatt_db, srvc_handle, &uuid, 0,
+ gatt_db_add_characteristic(gatt_db, srvc_handle, &uuid, GATT_PERM_READ,
GATT_CHR_PROP_READ,
device_info_read_cb, NULL,
(void *) device_info.model_number);
bt_uuid16_create(&uuid, GATT_CHARAC_SERIAL_NUMBER_STRING);
- gatt_db_add_characteristic(gatt_db, srvc_handle, &uuid, 0,
+ gatt_db_add_characteristic(gatt_db, srvc_handle, &uuid, GATT_PERM_READ,
GATT_CHR_PROP_READ,
device_info_read_cb, NULL,
(void *) device_info.serial_number);
bt_uuid16_create(&uuid, GATT_CHARAC_FIRMWARE_REVISION_STRING);
- gatt_db_add_characteristic(gatt_db, srvc_handle, &uuid, 0,
+ gatt_db_add_characteristic(gatt_db, srvc_handle, &uuid, GATT_PERM_READ,
GATT_CHR_PROP_READ,
device_info_read_cb, NULL,
(void *) device_info.firmware_rev);
bt_uuid16_create(&uuid, GATT_CHARAC_HARDWARE_REVISION_STRING);
- gatt_db_add_characteristic(gatt_db, srvc_handle, &uuid, 0,
+ gatt_db_add_characteristic(gatt_db, srvc_handle, &uuid, GATT_PERM_READ,
GATT_CHR_PROP_READ,
device_info_read_cb, NULL,
(void *) device_info.hardware_rev);
bt_uuid16_create(&uuid, GATT_CHARAC_SOFTWARE_REVISION_STRING);
- gatt_db_add_characteristic(gatt_db, srvc_handle, &uuid, 0,
+ gatt_db_add_characteristic(gatt_db, srvc_handle, &uuid, GATT_PERM_READ,
GATT_CHR_PROP_READ,
device_info_read_cb, NULL,
(void *) device_info.software_rev);
bt_uuid16_create(&uuid, GATT_CHARAC_MANUFACTURER_NAME_STRING);
- gatt_db_add_characteristic(gatt_db, srvc_handle, &uuid, 0,
+ gatt_db_add_characteristic(gatt_db, srvc_handle, &uuid, GATT_PERM_READ,
GATT_CHR_PROP_READ,
device_info_read_cb, NULL,
(void *) device_info.manufacturer_name);
@@ -5079,15 +6006,14 @@
gatt_db_service_set_active(gatt_db, srvc_handle, true);
}
-static void gatt_srvc_change_register_cb(uint16_t handle, uint16_t offset,
+static void gatt_srvc_change_write_cb(uint16_t handle, uint16_t offset,
const uint8_t *val, size_t len,
uint8_t att_opcode,
bdaddr_t *bdaddr,
void *user_data)
{
- uint8_t pdu[ATT_DEFAULT_LE_MTU];
+ struct pending_request *entry;
struct gatt_device *dev;
- uint16_t length;
dev = find_device_by_addr(bdaddr);
if (!dev) {
@@ -5095,14 +6021,53 @@
return;
}
- /* TODO handle CCC */
+ entry = queue_find(dev->pending_requests, match_dev_request_by_handle,
+ UINT_TO_PTR(handle));
+ if (!entry)
+ return;
- /* Set services changed notification flag */
- dev->notify_services_changed = !!(*val);
+ entry->state = REQUEST_DONE;
- length = enc_write_resp(pdu);
+ if (!bt_device_is_bonded(bdaddr)) {
+ entry->error = ATT_ECODE_AUTHORIZATION;
+ return;
+ }
- g_attrib_send(dev->attrib, 0, pdu, length, NULL, NULL, NULL);
+ /* Set services changed indication value */
+ bt_store_gatt_ccc(bdaddr, *val);
+}
+
+static void gatt_srvc_change_read_cb(uint16_t handle, uint16_t offset,
+ uint8_t att_opcode, bdaddr_t *bdaddr,
+ void *user_data)
+{
+ struct pending_request *entry;
+ struct gatt_device *dev;
+ uint16_t ccc = 0;
+
+ dev = find_device_by_addr(bdaddr);
+ if (!dev) {
+ error("gatt: Could not find device ?!");
+ return;
+ }
+
+ entry = queue_find(dev->pending_requests, match_dev_request_by_handle,
+ UINT_TO_PTR(handle));
+ if (!entry)
+ return;
+
+ ccc = bt_get_gatt_ccc(&dev->bdaddr);
+ entry->state = REQUEST_DONE;
+
+ entry->value = (uint8_t *) new0(uint16_t, 1);
+ if (!entry->value) {
+ entry->error = ATT_ECODE_INSUFF_RESOURCES;
+
+ return;
+ }
+
+ entry->length = sizeof(uint16_t);
+ memcpy(entry->value, &ccc, sizeof(ccc));
}
static void register_gatt_service(void)
@@ -5116,13 +6081,16 @@
srvc_handle = gatt_db_add_service(gatt_db, &uuid, true, 4);
bt_uuid16_create(&uuid, GATT_CHARAC_SERVICE_CHANGED);
- gatt_db_add_characteristic(gatt_db, srvc_handle, &uuid, 0,
+ service_changed_handle = gatt_db_add_characteristic(gatt_db,
+ srvc_handle, &uuid, 0,
GATT_CHR_PROP_INDICATE, NULL, NULL,
NULL);
bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID);
- gatt_db_add_char_descriptor(gatt_db, srvc_handle, &uuid, 0, NULL,
- gatt_srvc_change_register_cb, NULL);
+ gatt_db_add_char_descriptor(gatt_db, srvc_handle, &uuid,
+ GATT_PERM_READ | GATT_PERM_WRITE,
+ gatt_srvc_change_read_cb,
+ gatt_srvc_change_write_cb, NULL);
gatt_db_service_set_active(gatt_db, srvc_handle, true);
}
@@ -5132,7 +6100,7 @@
GError *gerr = NULL;
/* For now only listen on BLE */
- listening_io = bt_io_listen(connect_event, NULL,
+ listening_io = bt_io_listen(NULL, connect_confirm,
&listening_io, NULL, &gerr,
BT_IO_OPT_SOURCE_TYPE, BDADDR_LE_PUBLIC,
BT_IO_OPT_CID, ATT_CID,
@@ -5154,14 +6122,33 @@
if (!start_listening_io())
return false;
+ if (!bt_le_register(le_device_found_handler)) {
+ error("gatt: bt_le_register failed");
+
+ g_io_channel_unref(listening_io);
+ listening_io = NULL;
+
+ return false;
+ }
+
+ crypto = bt_crypto_new();
+ if (!crypto) {
+ error("gatt: Failed to setup crypto");
+
+ g_io_channel_unref(listening_io);
+ listening_io = NULL;
+
+ return false;
+ }
+
gatt_devices = queue_new();
gatt_apps = queue_new();
app_connections = queue_new();
listen_apps = queue_new();
gatt_db = gatt_db_new();
- if (!gatt_devices || !gatt_apps || !listen_apps ||
- !app_connections || !gatt_db) {
+ if (!gatt_devices || !gatt_apps || !listen_apps || !app_connections ||
+ !gatt_db) {
error("gatt: Failed to allocate memory for queues");
queue_destroy(gatt_apps, NULL);
@@ -5182,6 +6169,8 @@
g_io_channel_unref(listening_io);
listening_io = NULL;
+ bt_crypto_unref(crypto);
+
return false;
}
@@ -5223,4 +6212,72 @@
g_io_channel_unref(listening_io);
listening_io = NULL;
+
+ bt_crypto_unref(crypto);
+ crypto = NULL;
+
+ bt_le_unregister();
+}
+
+
+unsigned int bt_gatt_register_app(const char *uuid, gatt_type_t type,
+ gatt_conn_cb_t func)
+{
+ struct gatt_app *app;
+ bt_uuid_t uuid128;
+
+ bt_string_to_uuid(&uuid128, uuid);
+ app = register_app((void *) &uuid128.value.u128, type);
+ if (!app)
+ return 0;
+
+ app->func = func;
+
+ return app->id;
+}
+
+bool bt_gatt_unregister_app(unsigned int id)
+{
+ uint8_t status;
+
+ status = unregister_app(id);
+
+ return status != HAL_STATUS_FAILED;
+}
+
+bool bt_gatt_connect_app(unsigned int id, const bdaddr_t *addr)
+{
+ uint8_t status;
+
+ status = handle_connect(id, addr);
+
+ return status != HAL_STATUS_FAILED;
+}
+
+bool bt_gatt_disconnect_app(unsigned int id, const bdaddr_t *addr)
+{
+ struct app_connection match;
+ struct app_connection *conn;
+ struct gatt_device *device;
+ struct gatt_app *app;
+
+ app = find_app_by_id(id);
+ if (!app)
+ return false;
+
+ device = find_device_by_addr(addr);
+ if (!device)
+ return false;
+
+ match.device = device;
+ match.app = app;
+
+ conn = queue_find(app_connections, match_connection_by_device_and_app,
+ &match);
+ if (!conn)
+ return false;
+
+ trigger_disconnection(conn);
+
+ return true;
}
diff --git a/android/gatt.h b/android/gatt.h
index d4392d9..5ba9161 100644
--- a/android/gatt.h
+++ b/android/gatt.h
@@ -23,3 +23,18 @@
bool bt_gatt_register(struct ipc *ipc, const bdaddr_t *addr);
void bt_gatt_unregister(void);
+
+
+typedef enum {
+ GATT_CLIENT,
+ GATT_SERVER,
+} gatt_type_t;
+
+typedef void (*gatt_conn_cb_t)(const bdaddr_t *addr, int err, void *attrib);
+
+unsigned int bt_gatt_register_app(const char *uuid, gatt_type_t type,
+ gatt_conn_cb_t func);
+bool bt_gatt_unregister_app(unsigned int id);
+
+bool bt_gatt_connect_app(unsigned int id, const bdaddr_t *addr);
+bool bt_gatt_disconnect_app(unsigned int id, const bdaddr_t *addr);
diff --git a/android/hal-a2dp.c b/android/hal-a2dp.c
index 68888b2..87aebde 100644
--- a/android/hal-a2dp.c
+++ b/android/hal-a2dp.c
@@ -31,7 +31,7 @@
return cbs != NULL;
}
-static void handle_conn_state(void *buf, uint16_t len)
+static void handle_conn_state(void *buf, uint16_t len, int fd)
{
struct hal_ev_a2dp_conn_state *ev = buf;
@@ -40,7 +40,7 @@
(bt_bdaddr_t *) (ev->bdaddr));
}
-static void handle_audio_state(void *buf, uint16_t len)
+static void handle_audio_state(void *buf, uint16_t len, int fd)
{
struct hal_ev_a2dp_audio_state *ev = buf;
@@ -53,16 +53,10 @@
* index in table equals to 'opcode - HAL_MINIMUM_EVENT'
*/
static const struct hal_ipc_handler ev_handlers[] = {
- { /* HAL_EV_A2DP_CONN_STATE */
- .handler = handle_conn_state,
- .var_len = false,
- .data_len = sizeof(struct hal_ev_a2dp_conn_state),
- },
- { /* HAL_EV_A2DP_AUDIO_STATE */
- .handler = handle_audio_state,
- .var_len = false,
- .data_len = sizeof(struct hal_ev_a2dp_audio_state),
- },
+ /* HAL_EV_A2DP_CONN_STATE */
+ { handle_conn_state, false, sizeof(struct hal_ev_a2dp_conn_state) },
+ /* HAL_EV_A2DP_AUDIO_STATE */
+ { handle_audio_state, false, sizeof(struct hal_ev_a2dp_audio_state) },
};
static bt_status_t a2dp_connect(bt_bdaddr_t *bd_addr)
diff --git a/android/hal-audio-aptx.c b/android/hal-audio-aptx.c
new file mode 100644
index 0000000..09636b8
--- /dev/null
+++ b/android/hal-audio-aptx.c
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2014 Tieto Poland
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <malloc.h>
+#include <dlfcn.h>
+
+#include "audio-msg.h"
+#include "hal-audio.h"
+#include "hal-log.h"
+#include "src/shared/util.h"
+#include "profiles/audio/a2dp-codecs.h"
+
+#define APTX_SO_NAME "libbt-aptx.so"
+
+struct aptx_data {
+ a2dp_aptx_t aptx;
+
+ void *enc;
+};
+
+static const a2dp_aptx_t aptx_presets[] = {
+ {
+ .info = {
+ .vendor_id = APTX_VENDOR_ID,
+ .codec_id = APTX_CODEC_ID,
+ },
+ .frequency = APTX_SAMPLING_FREQ_44100 |
+ APTX_SAMPLING_FREQ_48000,
+ .channel_mode = APTX_CHANNEL_MODE_STEREO,
+ },
+ {
+ .info = {
+ .vendor_id = APTX_VENDOR_ID,
+ .codec_id = APTX_CODEC_ID,
+ },
+ .frequency = APTX_SAMPLING_FREQ_48000,
+ .channel_mode = APTX_CHANNEL_MODE_STEREO,
+ },
+ {
+ .info = {
+ .vendor_id = APTX_VENDOR_ID,
+ .codec_id = APTX_CODEC_ID,
+ },
+ .frequency = APTX_SAMPLING_FREQ_44100,
+ .channel_mode = APTX_CHANNEL_MODE_STEREO,
+ },
+};
+
+static void *aptx_handle;
+static int aptx_btencsize;
+static int (*aptx_init)(void *, short);
+static int (*aptx_encode)(void *, void *, void *, void *);
+
+static bool aptx_load(void)
+{
+ const char * (*aptx_version)(void);
+ const char * (*aptx_build)(void);
+ int (*aptx_sizeofenc)(void);
+
+ aptx_handle = dlopen(APTX_SO_NAME, RTLD_LAZY);
+ if (!aptx_handle) {
+ error("APTX: failed to open library %s (%s)", APTX_SO_NAME,
+ dlerror());
+ return false;
+ }
+
+ aptx_version = dlsym(aptx_handle, "aptxbtenc_version");
+ aptx_build = dlsym(aptx_handle, "aptxbtenc_build");
+
+ if (aptx_version && aptx_build)
+ info("APTX: using library version %s build %s", aptx_version(),
+ aptx_build());
+ else
+ warn("APTX: cannot retrieve library version");
+
+ aptx_sizeofenc = dlsym(aptx_handle, "SizeofAptxbtenc");
+ aptx_init = dlsym(aptx_handle, "aptxbtenc_init");
+ aptx_encode = dlsym(aptx_handle, "aptxbtenc_encodestereo");
+ if (!aptx_sizeofenc || !aptx_init || !aptx_encode) {
+ error("APTX: failed initialize library");
+ dlclose(aptx_handle);
+ aptx_handle = NULL;
+ return false;
+ }
+ aptx_btencsize = aptx_sizeofenc();
+
+ info("APTX: codec library initialized (size=%d)", aptx_btencsize);
+
+ return true;
+}
+
+static void aptx_unload(void)
+{
+ if (!aptx_handle)
+ return;
+
+ dlclose(aptx_handle);
+ aptx_handle = NULL;
+}
+
+static int aptx_get_presets(struct audio_preset *preset, size_t *len)
+{
+ int i;
+ int count;
+ size_t new_len = 0;
+ uint8_t *ptr = (uint8_t *) preset;
+ size_t preset_size = sizeof(*preset) + sizeof(a2dp_aptx_t);
+
+ DBG("");
+
+ count = sizeof(aptx_presets) / sizeof(aptx_presets[0]);
+
+ for (i = 0; i < count; i++) {
+ preset = (struct audio_preset *) ptr;
+
+ if (new_len + preset_size > *len)
+ break;
+
+ preset->len = sizeof(a2dp_aptx_t);
+ memcpy(preset->data, &aptx_presets[i], preset->len);
+
+ new_len += preset_size;
+ ptr += preset_size;
+ }
+
+ *len = new_len;
+
+ return i;
+}
+
+static bool aptx_codec_init(struct audio_preset *preset, uint16_t payload_len,
+ void **codec_data)
+{
+ struct aptx_data *aptx_data;
+
+ DBG("");
+
+ if (preset->len != sizeof(a2dp_aptx_t)) {
+ error("APTX: preset size mismatch");
+ return false;
+ }
+
+ aptx_data = new0(struct aptx_data, 1);
+ if (!aptx_data)
+ return false;
+
+ memcpy(&aptx_data->aptx, preset->data, preset->len);
+
+ aptx_data->enc = calloc(1, aptx_btencsize);
+ if (!aptx_data->enc) {
+ error("APTX: failed to create encoder");
+ free(aptx_data);
+ return false;
+ }
+
+ /* 1 = big-endian, this is what devices are using */
+ aptx_init(aptx_data->enc, 1);
+
+ *codec_data = aptx_data;
+
+ return true;
+}
+
+static bool aptx_cleanup(void *codec_data)
+{
+ struct aptx_data *aptx_data = (struct aptx_data *) codec_data;
+
+ free(aptx_data->enc);
+ free(codec_data);
+
+ return true;
+}
+
+static bool aptx_get_config(void *codec_data, struct audio_input_config *config)
+{
+ struct aptx_data *aptx_data = (struct aptx_data *) codec_data;
+
+ config->rate = aptx_data->aptx.frequency & APTX_SAMPLING_FREQ_48000 ?
+ 48000 : 44100;
+ config->channels = AUDIO_CHANNEL_OUT_STEREO;
+ config->format = AUDIO_FORMAT_PCM_16_BIT;
+
+ return true;
+}
+
+static size_t aptx_get_buffer_size(void *codec_data)
+{
+ /* TODO: return actual value */
+ return 0;
+}
+
+static size_t aptx_get_mediapacket_duration(void *codec_data)
+{
+ /* TODO: return actual value */
+ return 0;
+}
+
+static ssize_t aptx_encode_mediapacket(void *codec_data, const uint8_t *buffer,
+ size_t len, struct media_packet *mp,
+ size_t mp_data_len, size_t *written)
+{
+ struct aptx_data *aptx_data = (struct aptx_data *) codec_data;
+ const int16_t *ptr = (const void *) buffer;
+ size_t bytes_in = 0;
+ size_t bytes_out = 0;
+
+ while ((len - bytes_in) >= 16 && (mp_data_len - bytes_out) >= 4) {
+ int pcm_l[4], pcm_r[4];
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ pcm_l[i] = ptr[0];
+ pcm_r[i] = ptr[1];
+ ptr += 2;
+ }
+
+ aptx_encode(aptx_data->enc, pcm_l, pcm_r, &mp->data[bytes_out]);
+
+ bytes_in += 16;
+ bytes_out += 4;
+ }
+
+ *written = bytes_out;
+
+ return bytes_in;
+}
+
+static bool aptx_update_qos(void *codec_data, uint8_t op)
+{
+ /*
+ * aptX has constant bitrate of 352kbps (with constant 4:1 compression
+ * ratio) thus QoS is not possible here.
+ */
+
+ return false;
+}
+
+static const struct audio_codec codec = {
+ .type = A2DP_CODEC_VENDOR,
+ .use_rtp = false,
+
+ .load = aptx_load,
+ .unload = aptx_unload,
+
+ .get_presets = aptx_get_presets,
+
+ .init = aptx_codec_init,
+ .cleanup = aptx_cleanup,
+ .get_config = aptx_get_config,
+ .get_buffer_size = aptx_get_buffer_size,
+ .get_mediapacket_duration = aptx_get_mediapacket_duration,
+ .encode_mediapacket = aptx_encode_mediapacket,
+ .update_qos = aptx_update_qos,
+};
+
+const struct audio_codec *codec_aptx(void)
+{
+ return &codec;
+}
diff --git a/android/hal-audio-sbc.c b/android/hal-audio-sbc.c
new file mode 100644
index 0000000..0c31035
--- /dev/null
+++ b/android/hal-audio-sbc.c
@@ -0,0 +1,427 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdbool.h>
+#include <string.h>
+#include <malloc.h>
+
+#include <sbc/sbc.h>
+#include "audio-msg.h"
+#include "hal-audio.h"
+#include "hal-log.h"
+#include "../profiles/audio/a2dp-codecs.h"
+
+#define MAX_FRAMES_IN_PAYLOAD 15
+
+#define SBC_QUALITY_MIN_BITPOOL 33
+#define SBC_QUALITY_STEP 5
+
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+struct rtp_payload {
+ unsigned frame_count:4;
+ unsigned rfa0:1;
+ unsigned is_last_fragment:1;
+ unsigned is_first_fragment:1;
+ unsigned is_fragmented:1;
+} __attribute__ ((packed));
+
+#elif __BYTE_ORDER == __BIG_ENDIAN
+
+struct rtp_payload {
+ unsigned is_fragmented:1;
+ unsigned is_first_fragment:1;
+ unsigned is_last_fragment:1;
+ unsigned rfa0:1;
+ unsigned frame_count:4;
+} __attribute__ ((packed));
+
+#else
+#error "Unknown byte order"
+#endif
+
+struct media_packet_sbc {
+ struct media_packet_rtp hdr;
+ struct rtp_payload payload;
+ uint8_t data[0];
+};
+
+struct sbc_data {
+ a2dp_sbc_t sbc;
+
+ sbc_t enc;
+
+ uint16_t payload_len;
+
+ size_t in_frame_len;
+ size_t in_buf_size;
+
+ size_t out_frame_len;
+
+ unsigned frame_duration;
+ unsigned frames_per_packet;
+};
+
+static const a2dp_sbc_t sbc_presets[] = {
+ {
+ .frequency = SBC_SAMPLING_FREQ_44100 | SBC_SAMPLING_FREQ_48000,
+ .channel_mode = SBC_CHANNEL_MODE_MONO |
+ SBC_CHANNEL_MODE_DUAL_CHANNEL |
+ SBC_CHANNEL_MODE_STEREO |
+ SBC_CHANNEL_MODE_JOINT_STEREO,
+ .subbands = SBC_SUBBANDS_4 | SBC_SUBBANDS_8,
+ .allocation_method = SBC_ALLOCATION_SNR |
+ SBC_ALLOCATION_LOUDNESS,
+ .block_length = SBC_BLOCK_LENGTH_4 | SBC_BLOCK_LENGTH_8 |
+ SBC_BLOCK_LENGTH_12 | SBC_BLOCK_LENGTH_16,
+ .min_bitpool = MIN_BITPOOL,
+ .max_bitpool = MAX_BITPOOL
+ },
+ {
+ .frequency = SBC_SAMPLING_FREQ_44100,
+ .channel_mode = SBC_CHANNEL_MODE_JOINT_STEREO,
+ .subbands = SBC_SUBBANDS_8,
+ .allocation_method = SBC_ALLOCATION_LOUDNESS,
+ .block_length = SBC_BLOCK_LENGTH_16,
+ .min_bitpool = MIN_BITPOOL,
+ .max_bitpool = MAX_BITPOOL
+ },
+ {
+ .frequency = SBC_SAMPLING_FREQ_48000,
+ .channel_mode = SBC_CHANNEL_MODE_JOINT_STEREO,
+ .subbands = SBC_SUBBANDS_8,
+ .allocation_method = SBC_ALLOCATION_LOUDNESS,
+ .block_length = SBC_BLOCK_LENGTH_16,
+ .min_bitpool = MIN_BITPOOL,
+ .max_bitpool = MAX_BITPOOL
+ },
+};
+
+static int sbc_get_presets(struct audio_preset *preset, size_t *len)
+{
+ int i;
+ int count;
+ size_t new_len = 0;
+ uint8_t *ptr = (uint8_t *) preset;
+ size_t preset_size = sizeof(*preset) + sizeof(a2dp_sbc_t);
+
+ count = sizeof(sbc_presets) / sizeof(sbc_presets[0]);
+
+ for (i = 0; i < count; i++) {
+ preset = (struct audio_preset *) ptr;
+
+ if (new_len + preset_size > *len)
+ break;
+
+ preset->len = sizeof(a2dp_sbc_t);
+ memcpy(preset->data, &sbc_presets[i], preset->len);
+
+ new_len += preset_size;
+ ptr += preset_size;
+ }
+
+ *len = new_len;
+
+ return i;
+}
+
+static int sbc_freq2int(uint8_t freq)
+{
+ switch (freq) {
+ case SBC_SAMPLING_FREQ_16000:
+ return 16000;
+ case SBC_SAMPLING_FREQ_32000:
+ return 32000;
+ case SBC_SAMPLING_FREQ_44100:
+ return 44100;
+ case SBC_SAMPLING_FREQ_48000:
+ return 48000;
+ default:
+ return 0;
+ }
+}
+
+static const char *sbc_mode2str(uint8_t mode)
+{
+ switch (mode) {
+ case SBC_CHANNEL_MODE_MONO:
+ return "Mono";
+ case SBC_CHANNEL_MODE_DUAL_CHANNEL:
+ return "DualChannel";
+ case SBC_CHANNEL_MODE_STEREO:
+ return "Stereo";
+ case SBC_CHANNEL_MODE_JOINT_STEREO:
+ return "JointStereo";
+ default:
+ return "(unknown)";
+ }
+}
+
+static int sbc_blocks2int(uint8_t blocks)
+{
+ switch (blocks) {
+ case SBC_BLOCK_LENGTH_4:
+ return 4;
+ case SBC_BLOCK_LENGTH_8:
+ return 8;
+ case SBC_BLOCK_LENGTH_12:
+ return 12;
+ case SBC_BLOCK_LENGTH_16:
+ return 16;
+ default:
+ return 0;
+ }
+}
+
+static int sbc_subbands2int(uint8_t subbands)
+{
+ switch (subbands) {
+ case SBC_SUBBANDS_4:
+ return 4;
+ case SBC_SUBBANDS_8:
+ return 8;
+ default:
+ return 0;
+ }
+}
+
+static const char *sbc_allocation2str(uint8_t allocation)
+{
+ switch (allocation) {
+ case SBC_ALLOCATION_SNR:
+ return "SNR";
+ case SBC_ALLOCATION_LOUDNESS:
+ return "Loudness";
+ default:
+ return "(unknown)";
+ }
+}
+
+static void sbc_init_encoder(struct sbc_data *sbc_data)
+{
+ a2dp_sbc_t *in = &sbc_data->sbc;
+ sbc_t *out = &sbc_data->enc;
+
+ sbc_init_a2dp(out, 0L, in, sizeof(*in));
+
+ out->endian = SBC_LE;
+ out->bitpool = in->max_bitpool;
+
+ DBG("frequency=%d channel_mode=%s block_length=%d subbands=%d allocation=%s bitpool=%d-%d",
+ sbc_freq2int(in->frequency),
+ sbc_mode2str(in->channel_mode),
+ sbc_blocks2int(in->block_length),
+ sbc_subbands2int(in->subbands),
+ sbc_allocation2str(in->allocation_method),
+ in->min_bitpool, in->max_bitpool);
+}
+
+static void sbc_codec_calculate(struct sbc_data *sbc_data)
+{
+ size_t in_frame_len;
+ size_t out_frame_len;
+ size_t num_frames;
+
+ in_frame_len = sbc_get_codesize(&sbc_data->enc);
+ out_frame_len = sbc_get_frame_length(&sbc_data->enc);
+ num_frames = sbc_data->payload_len / out_frame_len;
+
+ if (num_frames > MAX_FRAMES_IN_PAYLOAD)
+ num_frames = MAX_FRAMES_IN_PAYLOAD;
+
+ sbc_data->in_frame_len = in_frame_len;
+ sbc_data->in_buf_size = num_frames * in_frame_len;
+
+ sbc_data->out_frame_len = out_frame_len;
+
+ sbc_data->frame_duration = sbc_get_frame_duration(&sbc_data->enc);
+ sbc_data->frames_per_packet = num_frames;
+
+ DBG("in_frame_len=%zu out_frame_len=%zu frames_per_packet=%zu",
+ in_frame_len, out_frame_len, num_frames);
+}
+
+static bool sbc_codec_init(struct audio_preset *preset, uint16_t payload_len,
+ void **codec_data)
+{
+ struct sbc_data *sbc_data;
+
+ if (preset->len != sizeof(a2dp_sbc_t)) {
+ error("SBC: preset size mismatch");
+ return false;
+ }
+
+ sbc_data = calloc(sizeof(struct sbc_data), 1);
+ if (!sbc_data)
+ return false;
+
+ memcpy(&sbc_data->sbc, preset->data, preset->len);
+
+ sbc_init_encoder(sbc_data);
+
+ sbc_data->payload_len = payload_len;
+
+ sbc_codec_calculate(sbc_data);
+
+ *codec_data = sbc_data;
+
+ return true;
+}
+
+static bool sbc_cleanup(void *codec_data)
+{
+ struct sbc_data *sbc_data = (struct sbc_data *) codec_data;
+
+ sbc_finish(&sbc_data->enc);
+ free(codec_data);
+
+ return true;
+}
+
+static bool sbc_get_config(void *codec_data, struct audio_input_config *config)
+{
+ struct sbc_data *sbc_data = (struct sbc_data *) codec_data;
+
+ switch (sbc_data->sbc.frequency) {
+ case SBC_SAMPLING_FREQ_16000:
+ config->rate = 16000;
+ break;
+ case SBC_SAMPLING_FREQ_32000:
+ config->rate = 32000;
+ break;
+ case SBC_SAMPLING_FREQ_44100:
+ config->rate = 44100;
+ break;
+ case SBC_SAMPLING_FREQ_48000:
+ config->rate = 48000;
+ break;
+ default:
+ return false;
+ }
+ config->channels = sbc_data->sbc.channel_mode == SBC_CHANNEL_MODE_MONO ?
+ AUDIO_CHANNEL_OUT_MONO :
+ AUDIO_CHANNEL_OUT_STEREO;
+ config->format = AUDIO_FORMAT_PCM_16_BIT;
+
+ return true;
+}
+
+static size_t sbc_get_buffer_size(void *codec_data)
+{
+ struct sbc_data *sbc_data = (struct sbc_data *) codec_data;
+
+ return sbc_data->in_buf_size;
+}
+
+static size_t sbc_get_mediapacket_duration(void *codec_data)
+{
+ struct sbc_data *sbc_data = (struct sbc_data *) codec_data;
+
+ return sbc_data->frame_duration * sbc_data->frames_per_packet;
+}
+
+static ssize_t sbc_encode_mediapacket(void *codec_data, const uint8_t *buffer,
+ size_t len, struct media_packet *mp,
+ size_t mp_data_len, size_t *written)
+{
+ struct sbc_data *sbc_data = (struct sbc_data *) codec_data;
+ struct media_packet_sbc *mp_sbc = (struct media_packet_sbc *) mp;
+ size_t consumed = 0;
+ size_t encoded = 0;
+ uint8_t frame_count = 0;
+
+ mp_data_len -= sizeof(mp_sbc->payload);
+
+ while (len - consumed >= sbc_data->in_frame_len &&
+ mp_data_len - encoded >= sbc_data->out_frame_len &&
+ frame_count < sbc_data->frames_per_packet) {
+ ssize_t read;
+ ssize_t written = 0;
+
+ read = sbc_encode(&sbc_data->enc, buffer + consumed,
+ sbc_data->in_frame_len, mp_sbc->data + encoded,
+ mp_data_len - encoded, &written);
+
+ if (read < 0) {
+ error("SBC: failed to encode block at frame %d (%zd)",
+ frame_count, read);
+ break;
+ }
+
+ frame_count++;
+ consumed += read;
+ encoded += written;
+ }
+
+ *written = encoded + sizeof(mp_sbc->payload);
+ mp_sbc->payload.frame_count = frame_count;
+
+ return consumed;
+}
+
+static bool sbc_update_qos(void *codec_data, uint8_t op)
+{
+ struct sbc_data *sbc_data = (struct sbc_data *) codec_data;
+ uint8_t curr_bitpool = sbc_data->enc.bitpool;
+ uint8_t new_bitpool = curr_bitpool;
+
+ switch (op) {
+ case QOS_POLICY_DEFAULT:
+ new_bitpool = sbc_data->sbc.max_bitpool;
+ break;
+
+ case QOS_POLICY_DECREASE:
+ if (curr_bitpool > SBC_QUALITY_MIN_BITPOOL) {
+ new_bitpool = curr_bitpool - SBC_QUALITY_STEP;
+ if (new_bitpool < SBC_QUALITY_MIN_BITPOOL)
+ new_bitpool = SBC_QUALITY_MIN_BITPOOL;
+ }
+ break;
+ }
+
+ if (new_bitpool == curr_bitpool)
+ return false;
+
+ sbc_data->enc.bitpool = new_bitpool;
+
+ sbc_codec_calculate(sbc_data);
+
+ info("SBC: bitpool changed: %d -> %d", curr_bitpool, new_bitpool);
+
+ return true;
+}
+
+static const struct audio_codec codec = {
+ .type = A2DP_CODEC_SBC,
+ .use_rtp = true,
+
+ .get_presets = sbc_get_presets,
+
+ .init = sbc_codec_init,
+ .cleanup = sbc_cleanup,
+ .get_config = sbc_get_config,
+ .get_buffer_size = sbc_get_buffer_size,
+ .get_mediapacket_duration = sbc_get_mediapacket_duration,
+ .encode_mediapacket = sbc_encode_mediapacket,
+ .update_qos = sbc_update_qos,
+};
+
+const struct audio_codec *codec_sbc(void)
+{
+ return &codec;
+}
diff --git a/android/hal-audio.c b/android/hal-audio.c
index 1c889cc..1a3d3ae 100644
--- a/android/hal-audio.c
+++ b/android/hal-audio.c
@@ -30,26 +30,20 @@
#include <hardware/audio.h>
#include <hardware/hardware.h>
-#include <sbc/sbc.h>
-
#include "audio-msg.h"
#include "ipc-common.h"
#include "hal-log.h"
#include "hal-msg.h"
-#include "../profiles/audio/a2dp-codecs.h"
-#include "../src/shared/util.h"
+#include "hal-audio.h"
+#include "src/shared/util.h"
+#include "src/shared/queue.h"
#define FIXED_A2DP_PLAYBACK_LATENCY_MS 25
#define FIXED_BUFFER_SIZE (20 * 512)
-#define MAX_FRAMES_IN_PAYLOAD 15
-
#define MAX_DELAY 100000 /* 100ms */
-#define SBC_QUALITY_MIN_BITPOOL 33
-#define SBC_QUALITY_STEP 5
-
static const uint8_t a2dp_src_uuid[] = {
0x00, 0x00, 0x11, 0x0a, 0x00, 0x00, 0x10, 0x00,
0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb };
@@ -60,88 +54,6 @@
static pthread_t ipc_th = 0;
static pthread_mutex_t sk_mutex = PTHREAD_MUTEX_INITIALIZER;
-#if __BYTE_ORDER == __LITTLE_ENDIAN
-
-struct rtp_header {
- unsigned cc:4;
- unsigned x:1;
- unsigned p:1;
- unsigned v:2;
-
- unsigned pt:7;
- unsigned m:1;
-
- uint16_t sequence_number;
- uint32_t timestamp;
- uint32_t ssrc;
- uint32_t csrc[0];
-} __attribute__ ((packed));
-
-struct rtp_payload {
- unsigned frame_count:4;
- unsigned rfa0:1;
- unsigned is_last_fragment:1;
- unsigned is_first_fragment:1;
- unsigned is_fragmented:1;
-} __attribute__ ((packed));
-
-#elif __BYTE_ORDER == __BIG_ENDIAN
-
-struct rtp_header {
- unsigned v:2;
- unsigned p:1;
- unsigned x:1;
- unsigned cc:4;
-
- unsigned m:1;
- unsigned pt:7;
-
- uint16_t sequence_number;
- uint32_t timestamp;
- uint32_t ssrc;
- uint32_t csrc[0];
-} __attribute__ ((packed));
-
-struct rtp_payload {
- unsigned is_fragmented:1;
- unsigned is_first_fragment:1;
- unsigned is_last_fragment:1;
- unsigned rfa0:1;
- unsigned frame_count:4;
-} __attribute__ ((packed));
-
-#else
-#error "Unknown byte order"
-#endif
-
-struct media_packet {
- struct rtp_header hdr;
- struct rtp_payload payload;
- uint8_t data[0];
-};
-
-struct audio_input_config {
- uint32_t rate;
- uint32_t channels;
- audio_format_t format;
-};
-
-struct sbc_data {
- a2dp_sbc_t sbc;
-
- sbc_t enc;
-
- uint16_t payload_len;
-
- size_t in_frame_len;
- size_t in_buf_size;
-
- size_t out_frame_len;
-
- unsigned frame_duration;
- unsigned frames_per_packet;
-};
-
static void timespec_add(struct timespec *base, uint64_t time_us,
struct timespec *res)
{
@@ -185,59 +97,17 @@
struct timespec *remain);
#endif
-static int sbc_get_presets(struct audio_preset *preset, size_t *len);
-static int sbc_codec_init(struct audio_preset *preset, uint16_t mtu,
- void **codec_data);
-static int sbc_cleanup(void *codec_data);
-static int sbc_get_config(void *codec_data, struct audio_input_config *config);
-static size_t sbc_get_buffer_size(void *codec_data);
-static size_t sbc_get_mediapacket_duration(void *codec_data);
-static ssize_t sbc_encode_mediapacket(void *codec_data, const uint8_t *buffer,
- size_t len, struct media_packet *mp,
- size_t mp_data_len, size_t *written);
-static bool sbc_update_qos(void *codec_data, uint8_t op);
-
-#define QOS_POLICY_DEFAULT 0x00
-#define QOS_POLICY_DECREASE 0x01
-
-struct audio_codec {
- uint8_t type;
-
- int (*get_presets) (struct audio_preset *preset, size_t *len);
-
- int (*init) (struct audio_preset *preset, uint16_t mtu,
- void **codec_data);
- int (*cleanup) (void *codec_data);
- int (*get_config) (void *codec_data,
- struct audio_input_config *config);
- size_t (*get_buffer_size) (void *codec_data);
- size_t (*get_mediapacket_duration) (void *codec_data);
- ssize_t (*encode_mediapacket) (void *codec_data, const uint8_t *buffer,
- size_t len, struct media_packet *mp,
- size_t mp_data_len, size_t *written);
- bool (*update_qos) (void *codec_data, uint8_t op);
-};
-
-static const struct audio_codec audio_codecs[] = {
- {
- .type = A2DP_CODEC_SBC,
-
- .get_presets = sbc_get_presets,
-
- .init = sbc_codec_init,
- .cleanup = sbc_cleanup,
- .get_config = sbc_get_config,
- .get_buffer_size = sbc_get_buffer_size,
- .get_mediapacket_duration = sbc_get_mediapacket_duration,
- .encode_mediapacket = sbc_encode_mediapacket,
- .update_qos = sbc_update_qos,
- }
+static const audio_codec_get_t audio_codecs[] = {
+ codec_aptx,
+ codec_sbc,
};
#define NUM_CODECS (sizeof(audio_codecs) / sizeof(audio_codecs[0]))
#define MAX_AUDIO_ENDPOINTS NUM_CODECS
+static struct queue *loaded_codecs;
+
struct audio_endpoint {
uint8_t id;
const struct audio_codec *codec;
@@ -278,330 +148,6 @@
struct a2dp_stream_out *out;
};
-static const a2dp_sbc_t sbc_presets[] = {
- {
- .frequency = SBC_SAMPLING_FREQ_44100 | SBC_SAMPLING_FREQ_48000,
- .channel_mode = SBC_CHANNEL_MODE_MONO |
- SBC_CHANNEL_MODE_DUAL_CHANNEL |
- SBC_CHANNEL_MODE_STEREO |
- SBC_CHANNEL_MODE_JOINT_STEREO,
- .subbands = SBC_SUBBANDS_4 | SBC_SUBBANDS_8,
- .allocation_method = SBC_ALLOCATION_SNR |
- SBC_ALLOCATION_LOUDNESS,
- .block_length = SBC_BLOCK_LENGTH_4 | SBC_BLOCK_LENGTH_8 |
- SBC_BLOCK_LENGTH_12 | SBC_BLOCK_LENGTH_16,
- .min_bitpool = MIN_BITPOOL,
- .max_bitpool = MAX_BITPOOL
- },
- {
- .frequency = SBC_SAMPLING_FREQ_44100,
- .channel_mode = SBC_CHANNEL_MODE_JOINT_STEREO,
- .subbands = SBC_SUBBANDS_8,
- .allocation_method = SBC_ALLOCATION_LOUDNESS,
- .block_length = SBC_BLOCK_LENGTH_16,
- .min_bitpool = MIN_BITPOOL,
- .max_bitpool = MAX_BITPOOL
- },
- {
- .frequency = SBC_SAMPLING_FREQ_48000,
- .channel_mode = SBC_CHANNEL_MODE_JOINT_STEREO,
- .subbands = SBC_SUBBANDS_8,
- .allocation_method = SBC_ALLOCATION_LOUDNESS,
- .block_length = SBC_BLOCK_LENGTH_16,
- .min_bitpool = MIN_BITPOOL,
- .max_bitpool = MAX_BITPOOL
- },
-};
-
-static int sbc_get_presets(struct audio_preset *preset, size_t *len)
-{
- int i;
- int count;
- size_t new_len = 0;
- uint8_t *ptr = (uint8_t *) preset;
- size_t preset_size = sizeof(*preset) + sizeof(a2dp_sbc_t);
-
- count = sizeof(sbc_presets) / sizeof(sbc_presets[0]);
-
- for (i = 0; i < count; i++) {
- preset = (struct audio_preset *) ptr;
-
- if (new_len + preset_size > *len)
- break;
-
- preset->len = sizeof(a2dp_sbc_t);
- memcpy(preset->data, &sbc_presets[i], preset->len);
-
- new_len += preset_size;
- ptr += preset_size;
- }
-
- *len = new_len;
-
- return i;
-}
-
-static int sbc_freq2int(uint8_t freq)
-{
- switch (freq) {
- case SBC_SAMPLING_FREQ_16000:
- return 16000;
- case SBC_SAMPLING_FREQ_32000:
- return 32000;
- case SBC_SAMPLING_FREQ_44100:
- return 44100;
- case SBC_SAMPLING_FREQ_48000:
- return 48000;
- default:
- return 0;
- }
-}
-
-static const char *sbc_mode2str(uint8_t mode)
-{
- switch (mode) {
- case SBC_CHANNEL_MODE_MONO:
- return "Mono";
- case SBC_CHANNEL_MODE_DUAL_CHANNEL:
- return "DualChannel";
- case SBC_CHANNEL_MODE_STEREO:
- return "Stereo";
- case SBC_CHANNEL_MODE_JOINT_STEREO:
- return "JointStereo";
- default:
- return "(unknown)";
- }
-}
-
-static int sbc_blocks2int(uint8_t blocks)
-{
- switch (blocks) {
- case SBC_BLOCK_LENGTH_4:
- return 4;
- case SBC_BLOCK_LENGTH_8:
- return 8;
- case SBC_BLOCK_LENGTH_12:
- return 12;
- case SBC_BLOCK_LENGTH_16:
- return 16;
- default:
- return 0;
- }
-}
-
-static int sbc_subbands2int(uint8_t subbands)
-{
- switch (subbands) {
- case SBC_SUBBANDS_4:
- return 4;
- case SBC_SUBBANDS_8:
- return 8;
- default:
- return 0;
- }
-}
-
-static const char *sbc_allocation2str(uint8_t allocation)
-{
- switch (allocation) {
- case SBC_ALLOCATION_SNR:
- return "SNR";
- case SBC_ALLOCATION_LOUDNESS:
- return "Loudness";
- default:
- return "(unknown)";
- }
-}
-
-static void sbc_init_encoder(struct sbc_data *sbc_data)
-{
- a2dp_sbc_t *in = &sbc_data->sbc;
- sbc_t *out = &sbc_data->enc;
-
- sbc_init_a2dp(out, 0L, in, sizeof(*in));
-
- out->endian = SBC_LE;
- out->bitpool = in->max_bitpool;
-
- DBG("frequency=%d channel_mode=%s block_length=%d subbands=%d "
- "allocation=%s bitpool=%d-%d",
- sbc_freq2int(in->frequency),
- sbc_mode2str(in->channel_mode),
- sbc_blocks2int(in->block_length),
- sbc_subbands2int(in->subbands),
- sbc_allocation2str(in->allocation_method),
- in->min_bitpool, in->max_bitpool);
-}
-
-static void sbc_codec_calculate(struct sbc_data *sbc_data)
-{
- size_t in_frame_len;
- size_t out_frame_len;
- size_t num_frames;
-
- in_frame_len = sbc_get_codesize(&sbc_data->enc);
- out_frame_len = sbc_get_frame_length(&sbc_data->enc);
- num_frames = sbc_data->payload_len / out_frame_len;
-
- sbc_data->in_frame_len = in_frame_len;
- sbc_data->in_buf_size = num_frames * in_frame_len;
-
- sbc_data->out_frame_len = out_frame_len;
-
- sbc_data->frame_duration = sbc_get_frame_duration(&sbc_data->enc);
- sbc_data->frames_per_packet = num_frames;
-
- DBG("in_frame_len=%zu out_frame_len=%zu frames_per_packet=%zu",
- in_frame_len, out_frame_len, num_frames);
-}
-
-static int sbc_codec_init(struct audio_preset *preset, uint16_t payload_len,
- void **codec_data)
-{
- struct sbc_data *sbc_data;
-
- if (preset->len != sizeof(a2dp_sbc_t)) {
- error("SBC: preset size mismatch");
- return AUDIO_STATUS_FAILED;
- }
-
- sbc_data = calloc(sizeof(struct sbc_data), 1);
- if (!sbc_data)
- return AUDIO_STATUS_FAILED;
-
- memcpy(&sbc_data->sbc, preset->data, preset->len);
-
- sbc_init_encoder(sbc_data);
-
- sbc_data->payload_len = payload_len;
-
- sbc_codec_calculate(sbc_data);
-
- *codec_data = sbc_data;
-
- return AUDIO_STATUS_SUCCESS;
-}
-
-static int sbc_cleanup(void *codec_data)
-{
- struct sbc_data *sbc_data = (struct sbc_data *) codec_data;
-
- sbc_finish(&sbc_data->enc);
- free(codec_data);
-
- return AUDIO_STATUS_SUCCESS;
-}
-
-static int sbc_get_config(void *codec_data, struct audio_input_config *config)
-{
- struct sbc_data *sbc_data = (struct sbc_data *) codec_data;
-
- switch (sbc_data->sbc.frequency) {
- case SBC_SAMPLING_FREQ_16000:
- config->rate = 16000;
- break;
- case SBC_SAMPLING_FREQ_32000:
- config->rate = 32000;
- break;
- case SBC_SAMPLING_FREQ_44100:
- config->rate = 44100;
- break;
- case SBC_SAMPLING_FREQ_48000:
- config->rate = 48000;
- break;
- default:
- return AUDIO_STATUS_FAILED;
- }
- config->channels = sbc_data->sbc.channel_mode == SBC_CHANNEL_MODE_MONO ?
- AUDIO_CHANNEL_OUT_MONO :
- AUDIO_CHANNEL_OUT_STEREO;
- config->format = AUDIO_FORMAT_PCM_16_BIT;
-
- return AUDIO_STATUS_SUCCESS;
-}
-
-static size_t sbc_get_buffer_size(void *codec_data)
-{
- struct sbc_data *sbc_data = (struct sbc_data *) codec_data;
-
- return sbc_data->in_buf_size;
-}
-
-static size_t sbc_get_mediapacket_duration(void *codec_data)
-{
- struct sbc_data *sbc_data = (struct sbc_data *) codec_data;
-
- return sbc_data->frame_duration * sbc_data->frames_per_packet;
-}
-
-static ssize_t sbc_encode_mediapacket(void *codec_data, const uint8_t *buffer,
- size_t len, struct media_packet *mp,
- size_t mp_data_len, size_t *written)
-{
- struct sbc_data *sbc_data = (struct sbc_data *) codec_data;
- size_t consumed = 0;
- size_t encoded = 0;
- uint8_t frame_count = 0;
-
- while (len - consumed >= sbc_data->in_frame_len &&
- mp_data_len - encoded >= sbc_data->out_frame_len &&
- frame_count < MAX_FRAMES_IN_PAYLOAD) {
- ssize_t read;
- ssize_t written = 0;
-
- read = sbc_encode(&sbc_data->enc, buffer + consumed,
- sbc_data->in_frame_len, mp->data + encoded,
- mp_data_len - encoded, &written);
-
- if (read < 0) {
- error("SBC: failed to encode block at frame %d (%zd)",
- frame_count, read);
- break;
- }
-
- frame_count++;
- consumed += read;
- encoded += written;
- }
-
- *written = encoded;
- mp->payload.frame_count = frame_count;
-
- return consumed;
-}
-
-static bool sbc_update_qos(void *codec_data, uint8_t op)
-{
- struct sbc_data *sbc_data = (struct sbc_data *) codec_data;
- uint8_t curr_bitpool = sbc_data->enc.bitpool;
- uint8_t new_bitpool = curr_bitpool;
-
- switch (op) {
- case QOS_POLICY_DEFAULT:
- new_bitpool = sbc_data->sbc.max_bitpool;
- break;
-
- case QOS_POLICY_DECREASE:
- if (curr_bitpool > SBC_QUALITY_MIN_BITPOOL) {
- new_bitpool = curr_bitpool - SBC_QUALITY_STEP;
- if (new_bitpool < SBC_QUALITY_MIN_BITPOOL)
- new_bitpool = SBC_QUALITY_MIN_BITPOOL;
- }
- break;
- }
-
- if (new_bitpool == curr_bitpool)
- return false;
-
- sbc_data->enc.bitpool = new_bitpool;
-
- sbc_codec_calculate(sbc_data);
-
- info("SBC: bitpool changed: %d -> %d", curr_bitpool, new_bitpool);
-
- return true;
-}
-
static int audio_ipc_cmd(uint8_t service_id, uint8_t opcode, uint16_t len,
void *param, size_t *rsp_len, void *rsp, int *fd)
{
@@ -795,7 +341,7 @@
return result;
}
-static int ipc_open_stream_cmd(uint8_t endpoint_id, uint16_t *mtu, int *fd,
+static int ipc_open_stream_cmd(uint8_t *endpoint_id, uint16_t *mtu, int *fd,
struct audio_preset **caps)
{
char buf[BLUEZ_AUDIO_MTU];
@@ -810,13 +356,14 @@
if (!caps)
return AUDIO_STATUS_FAILED;
- cmd.id = endpoint_id;
+ cmd.id = *endpoint_id;
result = audio_ipc_cmd(AUDIO_SERVICE_ID, AUDIO_OP_OPEN_STREAM,
sizeof(cmd), &cmd, &rsp_len, rsp, fd);
if (result == AUDIO_STATUS_SUCCESS) {
size_t buf_len = sizeof(struct audio_preset) +
rsp->preset[0].len;
+ *endpoint_id = rsp->id;
*mtu = rsp->mtu;
*caps = malloc(buf_len);
memcpy(*caps, &rsp->preset, buf_len);
@@ -872,25 +419,46 @@
return result;
}
-static int register_endpoints(void)
+struct register_state {
+ struct audio_endpoint *ep;
+ bool error;
+};
+
+static void register_endpoint(void *data, void *user_data)
{
- struct audio_endpoint *ep = &audio_endpoints[0];
- size_t i;
+ struct audio_codec *codec = data;
+ struct register_state *state = user_data;
+ struct audio_endpoint *ep = state->ep;
- for (i = 0; i < NUM_CODECS; i++, ep++) {
- const struct audio_codec *codec = &audio_codecs[i];
+ /* don't even try to register more endpoints if one failed */
+ if (state->error)
+ return;
- ep->id = ipc_open_cmd(codec);
+ ep->id = ipc_open_cmd(codec);
- if (!ep->id)
- return AUDIO_STATUS_FAILED;
-
- ep->codec = codec;
- ep->codec_data = NULL;
- ep->fd = -1;
+ if (!ep->id) {
+ state->error = true;
+ error("Failed to register endpoint");
+ return;
}
- return AUDIO_STATUS_SUCCESS;
+ ep->codec = codec;
+ ep->codec_data = NULL;
+ ep->fd = -1;
+
+ state->ep++;
+}
+
+static int register_endpoints(void)
+{
+ struct register_state state;
+
+ state.ep = &audio_endpoints[0];
+ state.error = false;
+
+ queue_foreach(loaded_codecs, register_endpoint, &state);
+
+ return state.error ? AUDIO_STATUS_FAILED : AUDIO_STATUS_SUCCESS;
}
static void unregister_endpoints(void)
@@ -907,22 +475,43 @@
}
}
-static bool open_endpoint(struct audio_endpoint *ep,
+static bool open_endpoint(struct audio_endpoint **epp,
struct audio_input_config *cfg)
{
struct audio_preset *preset;
+ struct audio_endpoint *ep = *epp;
const struct audio_codec *codec;
uint16_t mtu;
uint16_t payload_len;
int fd;
+ size_t i;
+ uint8_t ep_id = 0;
- if (ipc_open_stream_cmd(ep->id, &mtu, &fd, &preset) !=
+ if (ep)
+ ep_id = ep->id;
+
+ if (ipc_open_stream_cmd(&ep_id, &mtu, &fd, &preset) !=
AUDIO_STATUS_SUCCESS)
return false;
- DBG("mtu=%u", mtu);
+ DBG("ep_id=%d mtu=%u", ep_id, mtu);
- payload_len = mtu - sizeof(*ep->mp);
+ for (i = 0; i < MAX_AUDIO_ENDPOINTS; i++)
+ if (audio_endpoints[i].id == ep_id) {
+ ep = &audio_endpoints[i];
+ break;
+ }
+
+ if (!ep) {
+ error("Cound not find opened endpoint");
+ goto failed;
+ }
+
+ *epp = ep;
+
+ payload_len = mtu;
+ if (ep->codec->use_rtp)
+ payload_len -= sizeof(struct rtp_header);
ep->fd = fd;
@@ -933,9 +522,14 @@
ep->mp = calloc(mtu, 1);
if (!ep->mp)
goto failed;
- ep->mp->hdr.v = 2;
- ep->mp->hdr.pt = 1;
- ep->mp->hdr.ssrc = htonl(1);
+
+ if (ep->codec->use_rtp) {
+ struct media_packet_rtp *mp_rtp =
+ (struct media_packet_rtp *) ep->mp;
+ mp_rtp->hdr.v = 2;
+ mp_rtp->hdr.pt = 0x60;
+ mp_rtp->hdr.ssrc = htonl(1);
+ }
ep->mp_data_len = payload_len;
@@ -982,9 +576,12 @@
{
const int16_t *input = (const void *) buffer;
int16_t *output = (void *) out->downmix_buf;
- size_t i;
+ size_t i, frames;
- for (i = 0; i < bytes / 2; i++) {
+ /* PCM 16bit stereo */
+ frames = bytes / (2 * sizeof(int16_t));
+
+ for (i = 0; i < frames; i++) {
int16_t l = le16_to_cpu(get_unaligned(&input[i * 2]));
int16_t r = le16_to_cpu(get_unaligned(&input[i * 2 + 1]));
@@ -1026,7 +623,7 @@
int ret;
while (true) {
- ret = write(ep->fd, mp, sizeof(*mp) + bytes);
+ ret = write(ep->fd, mp, bytes);
if (ret >= 0)
break;
@@ -1056,6 +653,7 @@
{
struct audio_endpoint *ep = out->ep;
struct media_packet *mp = (struct media_packet *) ep->mp;
+ struct media_packet_rtp *mp_rtp = (struct media_packet_rtp *) ep->mp;
size_t free_space = ep->mp_data_len;
size_t consumed = 0;
@@ -1072,8 +670,10 @@
* prepare media packet in advance so we don't waste time after
* wakeup
*/
- mp->hdr.sequence_number = htons(ep->seq++);
- mp->hdr.timestamp = htonl(ep->samples);
+ if (ep->codec->use_rtp) {
+ mp_rtp->hdr.sequence_number = htons(ep->seq++);
+ mp_rtp->hdr.timestamp = htonl(ep->samples);
+ }
read = ep->codec->encode_mediapacket(ep->codec_data,
buffer + consumed,
bytes - consumed, mp,
@@ -1131,17 +731,25 @@
}
}
- /* in resync mode we'll just drop mediapackets */
- if (!ep->resync) {
+ /* we send data only in case codec encoded some data, i.e. some
+ * codecs do internal buffering and output data only if full
+ * frame can be encoded
+ * in resync mode we'll just drop mediapackets
+ */
+ if (written > 0 && !ep->resync) {
/* wait some time for socket to be ready for write,
* but we'll just skip writing data if timeout occurs
*/
if (!wait_for_endpoint(ep, &do_write))
return false;
- if (do_write)
+ if (do_write) {
+ if (ep->codec->use_rtp)
+ written += sizeof(struct rtp_header);
+
if (!write_to_endpoint(ep, written))
return false;
+ }
}
/*
@@ -1528,10 +1136,10 @@
out->stream.write = out_write;
out->stream.get_render_position = out_get_render_position;
- /* TODO: for now we always use endpoint 0 */
- out->ep = &audio_endpoints[0];
+ /* We want to autoselect opened endpoint */
+ out->ep = NULL;
- if (!open_endpoint(out->ep, &out->cfg))
+ if (!open_endpoint(&out->ep, &out->cfg))
goto fail;
DBG("rate=%d channels=%d format=%d", out->cfg.rate,
@@ -1686,6 +1294,14 @@
return -ENOSYS;
}
+static void unload_codec(void *data)
+{
+ struct audio_codec *codec = data;
+
+ if (codec->unload)
+ codec->unload();
+}
+
static int audio_close(hw_device_t *device)
{
struct a2dp_audio_dev *a2dp_dev = (struct a2dp_audio_dev *)device;
@@ -1694,6 +1310,9 @@
unregister_endpoints();
+ queue_destroy(loaded_codecs, unload_codec);
+ loaded_codecs = NULL;
+
shutdown(listen_sk, SHUT_RDWR);
shutdown(audio_sk, SHUT_RDWR);
@@ -1833,6 +1452,7 @@
hw_device_t **device)
{
struct a2dp_audio_dev *a2dp_dev;
+ size_t i;
int err;
DBG("");
@@ -1871,6 +1491,18 @@
a2dp_dev->dev.close_input_stream = audio_close_input_stream;
a2dp_dev->dev.dump = audio_dump;
+ loaded_codecs = queue_new();
+
+ for (i = 0; i < NUM_CODECS; i++) {
+ audio_codec_get_t get_codec = audio_codecs[i];
+ const struct audio_codec *codec = get_codec();
+
+ if (codec->load && !codec->load())
+ continue;
+
+ queue_push_tail(loaded_codecs, (void *) codec);
+ }
+
/*
* Note that &a2dp_dev->dev.common is the same pointer as a2dp_dev.
* This results from the structure of following structs:a2dp_audio_dev,
diff --git a/android/hal-audio.h b/android/hal-audio.h
new file mode 100644
index 0000000..2b47412
--- /dev/null
+++ b/android/hal-audio.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <time.h>
+#include <hardware/audio.h>
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+struct rtp_header {
+ unsigned cc:4;
+ unsigned x:1;
+ unsigned p:1;
+ unsigned v:2;
+
+ unsigned pt:7;
+ unsigned m:1;
+
+ uint16_t sequence_number;
+ uint32_t timestamp;
+ uint32_t ssrc;
+ uint32_t csrc[0];
+} __attribute__ ((packed));
+
+#elif __BYTE_ORDER == __BIG_ENDIAN
+
+struct rtp_header {
+ unsigned v:2;
+ unsigned p:1;
+ unsigned x:1;
+ unsigned cc:4;
+
+ unsigned m:1;
+ unsigned pt:7;
+
+ uint16_t sequence_number;
+ uint32_t timestamp;
+ uint32_t ssrc;
+ uint32_t csrc[0];
+} __attribute__ ((packed));
+
+#else
+#error "Unknown byte order"
+#endif
+
+struct media_packet {
+ uint8_t data[0];
+};
+
+struct media_packet_rtp {
+ struct rtp_header hdr;
+ uint8_t data[0];
+};
+
+struct audio_input_config {
+ uint32_t rate;
+ uint32_t channels;
+ audio_format_t format;
+};
+
+struct audio_codec {
+ uint8_t type;
+ bool use_rtp;
+
+ bool (*load) (void);
+ void (*unload) (void);
+
+ int (*get_presets) (struct audio_preset *preset, size_t *len);
+
+ bool (*init) (struct audio_preset *preset, uint16_t mtu,
+ void **codec_data);
+ bool (*cleanup) (void *codec_data);
+ bool (*get_config) (void *codec_data,
+ struct audio_input_config *config);
+ size_t (*get_buffer_size) (void *codec_data);
+ size_t (*get_mediapacket_duration) (void *codec_data);
+ ssize_t (*encode_mediapacket) (void *codec_data, const uint8_t *buffer,
+ size_t len, struct media_packet *mp,
+ size_t mp_data_len, size_t *written);
+ bool (*update_qos) (void *codec_data, uint8_t op);
+};
+
+#define QOS_POLICY_DEFAULT 0x00
+#define QOS_POLICY_DECREASE 0x01
+
+typedef const struct audio_codec * (*audio_codec_get_t) (void);
+
+const struct audio_codec *codec_sbc(void);
+const struct audio_codec *codec_aptx(void);
diff --git a/android/hal-avrcp.c b/android/hal-avrcp.c
index 5e07366..09f5463 100644
--- a/android/hal-avrcp.c
+++ b/android/hal-avrcp.c
@@ -33,7 +33,7 @@
return cbs != NULL;
}
-static void handle_remote_features(void *buf, uint16_t len)
+static void handle_remote_features(void *buf, uint16_t len, int fd)
{
struct hal_ev_avrcp_remote_features *ev = buf;
@@ -42,19 +42,19 @@
ev->features);
}
-static void handle_get_play_status(void *buf, uint16_t len)
+static void handle_get_play_status(void *buf, uint16_t len, int fd)
{
if (cbs->get_play_status_cb)
cbs->get_play_status_cb();
}
-static void handle_list_player_attrs(void *buf, uint16_t len)
+static void handle_list_player_attrs(void *buf, uint16_t len, int fd)
{
if (cbs->list_player_app_attr_cb)
cbs->list_player_app_attr_cb();
}
-static void handle_list_player_values(void *buf, uint16_t len)
+static void handle_list_player_values(void *buf, uint16_t len, int fd)
{
struct hal_ev_avrcp_list_player_values *ev = buf;
@@ -62,7 +62,7 @@
cbs->list_player_app_values_cb(ev->attr);
}
-static void handle_get_player_values(void *buf, uint16_t len)
+static void handle_get_player_values(void *buf, uint16_t len, int fd)
{
struct hal_ev_avrcp_get_player_values *ev = buf;
btrc_player_attr_t attrs[4];
@@ -78,7 +78,7 @@
cbs->get_player_app_value_cb(ev->number, attrs);
}
-static void handle_get_player_attrs_text(void *buf, uint16_t len)
+static void handle_get_player_attrs_text(void *buf, uint16_t len, int fd)
{
struct hal_ev_avrcp_get_player_attrs_text *ev = buf;
btrc_player_attr_t attrs[4];
@@ -94,7 +94,7 @@
cbs->get_player_app_attrs_text_cb(ev->number, attrs);
}
-static void handle_get_player_values_text(void *buf, uint16_t len)
+static void handle_get_player_values_text(void *buf, uint16_t len, int fd)
{
struct hal_ev_avrcp_get_player_values_text *ev = buf;
@@ -103,7 +103,7 @@
ev->values);
}
-static void handle_set_player_value(void *buf, uint16_t len)
+static void handle_set_player_value(void *buf, uint16_t len, int fd)
{
struct hal_ev_avrcp_set_player_values *ev = buf;
struct hal_avrcp_player_attr_value *attrs;
@@ -125,7 +125,7 @@
cbs->set_player_app_value_cb(&values);
}
-static void handle_get_element_attrs(void *buf, uint16_t len)
+static void handle_get_element_attrs(void *buf, uint16_t len, int fd)
{
struct hal_ev_avrcp_get_element_attrs *ev = buf;
btrc_media_attr_t attrs[BTRC_MAX_APP_SETTINGS];
@@ -141,7 +141,7 @@
cbs->get_element_attr_cb(ev->number, attrs);
}
-static void handle_register_notification(void *buf, uint16_t len)
+static void handle_register_notification(void *buf, uint16_t len, int fd)
{
struct hal_ev_avrcp_register_notification *ev = buf;
@@ -149,7 +149,7 @@
cbs->register_notification_cb(ev->event, ev->param);
}
-static void handle_volume_changed(void *buf, uint16_t len)
+static void handle_volume_changed(void *buf, uint16_t len, int fd)
{
struct hal_ev_avrcp_volume_changed *ev = buf;
@@ -157,7 +157,7 @@
cbs->volume_change_cb(ev->volume, ev->type);
}
-static void handle_passthrough_cmd(void *buf, uint16_t len)
+static void handle_passthrough_cmd(void *buf, uint16_t len, int fd)
{
struct hal_ev_avrcp_passthrough_cmd *ev = buf;
diff --git a/android/hal-bluetooth.c b/android/hal-bluetooth.c
index 2155978..4291430 100644
--- a/android/hal-bluetooth.c
+++ b/android/hal-bluetooth.c
@@ -52,7 +52,7 @@
*hal_len = 1; \
} while (0)
-static void handle_adapter_state_changed(void *buf, uint16_t len)
+static void handle_adapter_state_changed(void *buf, uint16_t len, int fd)
{
struct hal_ev_adapter_state_changed *ev = buf;
@@ -197,7 +197,7 @@
exit(EXIT_FAILURE);
}
-static void handle_adapter_props_changed(void *buf, uint16_t len)
+static void handle_adapter_props_changed(void *buf, uint16_t len, int fd)
{
struct hal_ev_adapter_props_changed *ev = buf;
bt_property_t props[ev->num_props];
@@ -213,7 +213,7 @@
bt_hal_cbacks->adapter_properties_cb(ev->status, ev->num_props, props);
}
-static void handle_bond_state_change(void *buf, uint16_t len)
+static void handle_bond_state_change(void *buf, uint16_t len, int fd)
{
struct hal_ev_bond_state_changed *ev = buf;
bt_bdaddr_t *addr = (bt_bdaddr_t *) ev->bdaddr;
@@ -225,7 +225,7 @@
ev->state);
}
-static void handle_pin_request(void *buf, uint16_t len)
+static void handle_pin_request(void *buf, uint16_t len, int fd)
{
struct hal_ev_pin_request *ev = buf;
/* Those are declared as packed, so it's safe to assign pointers */
@@ -238,7 +238,7 @@
bt_hal_cbacks->pin_request_cb(addr, name, ev->class_of_dev);
}
-static void handle_ssp_request(void *buf, uint16_t len)
+static void handle_ssp_request(void *buf, uint16_t len, int fd)
{
struct hal_ev_ssp_request *ev = buf;
/* Those are declared as packed, so it's safe to assign pointers */
@@ -270,7 +270,7 @@
return bt_hal_cbacks != NULL;
}
-static void handle_discovery_state_changed(void *buf, uint16_t len)
+static void handle_discovery_state_changed(void *buf, uint16_t len, int fd)
{
struct hal_ev_discovery_state_changed *ev = buf;
@@ -280,7 +280,7 @@
bt_hal_cbacks->discovery_state_changed_cb(ev->state);
}
-static void handle_device_found(void *buf, uint16_t len)
+static void handle_device_found(void *buf, uint16_t len, int fd)
{
struct hal_ev_device_found *ev = buf;
bt_property_t props[ev->num_props];
@@ -296,7 +296,7 @@
bt_hal_cbacks->device_found_cb(ev->num_props, props);
}
-static void handle_device_state_changed(void *buf, uint16_t len)
+static void handle_device_state_changed(void *buf, uint16_t len, int fd)
{
struct hal_ev_remote_device_props *ev = buf;
bt_property_t props[ev->num_props];
@@ -314,7 +314,7 @@
ev->num_props, props);
}
-static void handle_acl_state_changed(void *buf, uint16_t len)
+static void handle_acl_state_changed(void *buf, uint16_t len, int fd)
{
struct hal_ev_acl_state_changed *ev = buf;
bt_bdaddr_t *addr = (bt_bdaddr_t *) ev->bdaddr;
@@ -326,7 +326,7 @@
ev->state);
}
-static void handle_dut_mode_receive(void *buf, uint16_t len)
+static void handle_dut_mode_receive(void *buf, uint16_t len, int fd)
{
struct hal_ev_dut_mode_receive *ev = buf;
@@ -341,7 +341,7 @@
bt_hal_cbacks->dut_mode_recv_cb(ev->opcode, ev->data, ev->len);
}
-static void handle_le_test_mode(void *buf, uint16_t len)
+static void handle_le_test_mode(void *buf, uint16_t len, int fd)
{
struct hal_ev_le_test_mode *ev = buf;
@@ -356,64 +356,38 @@
* index in table equals to 'opcode - HAL_MINIMUM_EVENT'
*/
static const struct hal_ipc_handler ev_handlers[] = {
- { /* HAL_EV_ADAPTER_STATE_CHANGED */
- .handler = handle_adapter_state_changed,
- .var_len = false,
- .data_len = sizeof(struct hal_ev_adapter_state_changed)
- },
- { /* HAL_EV_ADAPTER_PROPS_CHANGED */
- .handler = handle_adapter_props_changed,
- .var_len = true,
- .data_len = sizeof(struct hal_ev_adapter_props_changed) +
- sizeof(struct hal_property),
- },
- { /* HAL_EV_REMOTE_DEVICE_PROPS */
- .handler = handle_device_state_changed,
- .var_len = true,
- .data_len = sizeof(struct hal_ev_remote_device_props) +
- sizeof(struct hal_property),
- },
- { /* HAL_EV_DEVICE_FOUND */
- .handler = handle_device_found,
- .var_len = true,
- .data_len = sizeof(struct hal_ev_device_found) +
- sizeof(struct hal_property),
- },
- { /* HAL_EV_DISCOVERY_STATE_CHANGED */
- .handler = handle_discovery_state_changed,
- .var_len = false,
- .data_len = sizeof(struct hal_ev_discovery_state_changed),
- },
- { /* HAL_EV_PIN_REQUEST */
- .handler = handle_pin_request,
- .var_len = false,
- .data_len = sizeof(struct hal_ev_pin_request),
- },
- { /* HAL_EV_SSP_REQUEST */
- .handler = handle_ssp_request,
- .var_len = false,
- .data_len = sizeof(struct hal_ev_ssp_request),
- },
- { /* HAL_EV_BOND_STATE_CHANGED */
- .handler = handle_bond_state_change,
- .var_len = false,
- .data_len = sizeof(struct hal_ev_bond_state_changed),
- },
- { /* HAL_EV_ACL_STATE_CHANGED */
- .handler = handle_acl_state_changed,
- .var_len = false,
- .data_len = sizeof(struct hal_ev_acl_state_changed),
- },
- { /* HAL_EV_DUT_MODE_RECEIVE */
- .handler = handle_dut_mode_receive,
- .var_len = true,
- .data_len = sizeof(struct hal_ev_dut_mode_receive),
- },
- { /* HAL_EV_LE_TEST_MODE */
- .handler = handle_le_test_mode,
- .var_len = false,
- .data_len = sizeof(struct hal_ev_le_test_mode),
- }
+ /* HAL_EV_ADAPTER_STATE_CHANGED */
+ { handle_adapter_state_changed, false,
+ sizeof(struct hal_ev_adapter_state_changed) },
+ /* HAL_EV_ADAPTER_PROPS_CHANGED */
+ { handle_adapter_props_changed, true,
+ sizeof(struct hal_ev_adapter_props_changed) +
+ sizeof(struct hal_property) },
+ /* HAL_EV_REMOTE_DEVICE_PROPS */
+ { handle_device_state_changed, true,
+ sizeof(struct hal_ev_remote_device_props) +
+ sizeof(struct hal_property) },
+ /* HAL_EV_DEVICE_FOUND */
+ { handle_device_found, true, sizeof(struct hal_ev_device_found) +
+ sizeof(struct hal_property) },
+ /* HAL_EV_DISCOVERY_STATE_CHANGED */
+ { handle_discovery_state_changed, false,
+ sizeof(struct hal_ev_discovery_state_changed) },
+ /* HAL_EV_PIN_REQUEST */
+ { handle_pin_request, false, sizeof(struct hal_ev_pin_request) },
+ /* HAL_EV_SSP_REQUEST */
+ { handle_ssp_request, false, sizeof(struct hal_ev_ssp_request) },
+ /* HAL_EV_BOND_STATE_CHANGED */
+ { handle_bond_state_change, false,
+ sizeof(struct hal_ev_bond_state_changed) },
+ /* HAL_EV_ACL_STATE_CHANGED */
+ { handle_acl_state_changed, false,
+ sizeof(struct hal_ev_acl_state_changed) },
+ /* HAL_EV_DUT_MODE_RECEIVE */
+ { handle_dut_mode_receive, true,
+ sizeof(struct hal_ev_dut_mode_receive) },
+ /* HAL_EV_LE_TEST_MODE */
+ { handle_le_test_mode, false, sizeof(struct hal_ev_le_test_mode) },
};
static uint8_t get_mode(void)
@@ -441,12 +415,24 @@
if (interface_ready())
return BT_STATUS_DONE;
- bt_hal_cbacks = callbacks;
-
hal_ipc_register(HAL_SERVICE_ID_BLUETOOTH, ev_handlers,
sizeof(ev_handlers)/sizeof(ev_handlers[0]));
- if (!hal_ipc_init()) {
+ if (!hal_ipc_init(BLUEZ_HAL_SK_PATH, sizeof(BLUEZ_HAL_SK_PATH)))
+ return BT_STATUS_FAIL;
+
+ bt_hal_cbacks = callbacks;
+
+ /* Start Android Bluetooth daemon service */
+ if (property_set("bluetooth.start", "daemon") < 0) {
+ error("Failed to set bluetooth.start=daemon");
+ hal_ipc_cleanup();
+ bt_hal_cbacks = NULL;
+ return BT_STATUS_FAIL;
+ }
+
+ if (!hal_ipc_accept()) {
+ hal_ipc_cleanup();
bt_hal_cbacks = NULL;
return BT_STATUS_FAIL;
}
diff --git a/android/hal-gatt.c b/android/hal-gatt.c
index 93dc066..c22113f 100644
--- a/android/hal-gatt.c
+++ b/android/hal-gatt.c
@@ -63,7 +63,7 @@
/* Client Event Handlers */
-static void handle_register_client(void *buf, uint16_t len)
+static void handle_register_client(void *buf, uint16_t len, int fd)
{
struct hal_ev_gatt_client_register_client *ev = buf;
@@ -72,7 +72,7 @@
(bt_uuid_t *) ev->app_uuid);
}
-static void handle_scan_result(void *buf, uint16_t len)
+static void handle_scan_result(void *buf, uint16_t len, int fd)
{
struct hal_ev_gatt_client_scan_result *ev = buf;
uint8_t ad[62];
@@ -91,7 +91,7 @@
ad);
}
-static void handle_connect(void *buf, uint16_t len)
+static void handle_connect(void *buf, uint16_t len, int fd)
{
struct hal_ev_gatt_client_connect *ev = buf;
@@ -100,7 +100,7 @@
(bt_bdaddr_t *) ev->bda);
}
-static void handle_disconnect(void *buf, uint16_t len)
+static void handle_disconnect(void *buf, uint16_t len, int fd)
{
struct hal_ev_gatt_client_disconnect *ev = buf;
@@ -109,7 +109,7 @@
(bt_bdaddr_t *) ev->bda);
}
-static void handle_search_complete(void *buf, uint16_t len)
+static void handle_search_complete(void *buf, uint16_t len, int fd)
{
struct hal_ev_gatt_client_search_complete *ev = buf;
@@ -117,7 +117,7 @@
cbs->client->search_complete_cb(ev->conn_id, ev->status);
}
-static void handle_search_result(void *buf, uint16_t len)
+static void handle_search_result(void *buf, uint16_t len, int fd)
{
struct hal_ev_gatt_client_search_result *ev = buf;
btgatt_srvc_id_t srvc_id;
@@ -128,7 +128,7 @@
cbs->client->search_result_cb(ev->conn_id, &srvc_id);
}
-static void handle_get_characteristic(void *buf, uint16_t len)
+static void handle_get_characteristic(void *buf, uint16_t len, int fd)
{
struct hal_ev_gatt_client_get_characteristic *ev = buf;
btgatt_gatt_id_t char_id;
@@ -143,7 +143,7 @@
ev->char_prop);
}
-static void handle_get_descriptor(void *buf, uint16_t len)
+static void handle_get_descriptor(void *buf, uint16_t len, int fd)
{
struct hal_ev_gatt_client_get_descriptor *ev = buf;
btgatt_gatt_id_t descr_id;
@@ -159,7 +159,7 @@
&srvc_id, &char_id, &descr_id);
}
-static void handle_get_included_service(void *buf, uint16_t len)
+static void handle_get_included_service(void *buf, uint16_t len, int fd)
{
struct hal_ev_gatt_client_get_inc_service *ev = buf;
btgatt_srvc_id_t srvc_id;
@@ -174,7 +174,7 @@
&incl_srvc_id);
}
-static void handle_register_for_notification(void *buf, uint16_t len)
+static void handle_register_for_notification(void *buf, uint16_t len, int fd)
{
struct hal_ev_gatt_client_reg_for_notif *ev = buf;
btgatt_gatt_id_t char_id;
@@ -191,7 +191,7 @@
&char_id);
}
-static void handle_notify(void *buf, uint16_t len)
+static void handle_notify(void *buf, uint16_t len, int fd)
{
struct hal_ev_gatt_client_notify *ev = buf;
btgatt_notify_params_t params;
@@ -215,7 +215,7 @@
cbs->client->notify_cb(ev->conn_id, ¶ms);
}
-static void handle_read_characteristic(void *buf, uint16_t len)
+static void handle_read_characteristic(void *buf, uint16_t len, int fd)
{
struct hal_ev_gatt_client_read_characteristic *ev = buf;
btgatt_read_params_t params;
@@ -242,7 +242,7 @@
¶ms);
}
-static void handle_write_characteristic(void *buf, uint16_t len)
+static void handle_write_characteristic(void *buf, uint16_t len, int fd)
{
struct hal_ev_gatt_client_write_characteristic *ev = buf;
btgatt_write_params_t params;
@@ -260,7 +260,7 @@
¶ms);
}
-static void handle_read_descriptor(void *buf, uint16_t len)
+static void handle_read_descriptor(void *buf, uint16_t len, int fd)
{
struct hal_ev_gatt_client_read_descriptor *ev = buf;
btgatt_read_params_t params;
@@ -287,7 +287,7 @@
¶ms);
}
-static void handle_write_descriptor(void *buf, uint16_t len)
+static void handle_write_descriptor(void *buf, uint16_t len, int fd)
{
struct hal_ev_gatt_client_write_descriptor *ev = buf;
btgatt_write_params_t params;
@@ -305,7 +305,7 @@
¶ms);
}
-static void handle_execute_write(void *buf, uint16_t len)
+static void handle_execute_write(void *buf, uint16_t len, int fd)
{
struct hal_ev_gatt_client_exec_write *ev = buf;
@@ -313,7 +313,7 @@
cbs->client->execute_write_cb(ev->conn_id, ev->status);
}
-static void handle_read_remote_rssi(void *buf, uint16_t len)
+static void handle_read_remote_rssi(void *buf, uint16_t len, int fd)
{
struct hal_ev_gatt_client_read_remote_rssi *ev = buf;
@@ -323,7 +323,7 @@
ev->rssi, ev->status);
}
-static void handle_listen(void *buf, uint16_t len)
+static void handle_listen(void *buf, uint16_t len, int fd)
{
struct hal_ev_gatt_client_listen *ev = buf;
@@ -333,7 +333,7 @@
/* Server Event Handlers */
-static void handle_register_server(void *buf, uint16_t len)
+static void handle_register_server(void *buf, uint16_t len, int fd)
{
struct hal_ev_gatt_server_register *ev = buf;
@@ -342,7 +342,7 @@
(bt_uuid_t *) &ev->uuid);
}
-static void handle_connection(void *buf, uint16_t len)
+static void handle_connection(void *buf, uint16_t len, int fd)
{
struct hal_ev_gatt_server_connection *ev = buf;
@@ -352,7 +352,7 @@
(bt_bdaddr_t *) &ev->bdaddr);
}
-static void handle_service_added(void *buf, uint16_t len)
+static void handle_service_added(void *buf, uint16_t len, int fd)
{
struct hal_ev_gatt_server_service_added *ev = buf;
btgatt_srvc_id_t srvc_id;
@@ -364,7 +364,7 @@
&srvc_id, ev->srvc_handle);
}
-static void handle_included_service_added(void *buf, uint16_t len)
+static void handle_included_service_added(void *buf, uint16_t len, int fd)
{
struct hal_ev_gatt_server_inc_srvc_added *ev = buf;
@@ -375,7 +375,7 @@
ev->incl_srvc_handle);
}
-static void handle_characteristic_added(void *buf, uint16_t len)
+static void handle_characteristic_added(void *buf, uint16_t len, int fd)
{
struct hal_ev_gatt_server_characteristic_added *ev = buf;
@@ -386,7 +386,7 @@
ev->char_handle);
}
-static void handle_descriptor_added(void *buf, uint16_t len)
+static void handle_descriptor_added(void *buf, uint16_t len, int fd)
{
struct hal_ev_gatt_server_descriptor_added *ev = buf;
@@ -397,7 +397,7 @@
ev->descr_handle);
}
-static void handle_service_started(void *buf, uint16_t len)
+static void handle_service_started(void *buf, uint16_t len, int fd)
{
struct hal_ev_gatt_server_service_started *ev = buf;
@@ -406,7 +406,7 @@
ev->srvc_handle);
}
-static void handle_service_stopped(void *buf, uint16_t len)
+static void handle_service_stopped(void *buf, uint16_t len, int fd)
{
struct hal_ev_gatt_server_service_stopped *ev = buf;
@@ -415,7 +415,7 @@
ev->srvc_handle);
}
-static void handle_service_deleted(void *buf, uint16_t len)
+static void handle_service_deleted(void *buf, uint16_t len, int fd)
{
struct hal_ev_gatt_server_service_deleted *ev = buf;
@@ -424,7 +424,7 @@
ev->srvc_handle);
}
-static void handle_request_read(void *buf, uint16_t len)
+static void handle_request_read(void *buf, uint16_t len, int fd)
{
struct hal_ev_gatt_server_request_read *ev = buf;
@@ -435,7 +435,7 @@
ev->is_long);
}
-static void handle_request_write(void *buf, uint16_t len)
+static void handle_request_write(void *buf, uint16_t len, int fd)
{
struct hal_ev_gatt_server_request_write *ev = buf;
@@ -452,7 +452,7 @@
ev->is_prep, ev->value);
}
-static void handle_request_exec_write(void *buf, uint16_t len)
+static void handle_request_exec_write(void *buf, uint16_t len, int fd)
{
struct hal_ev_gatt_server_request_exec_write *ev = buf;
@@ -462,7 +462,7 @@
ev->exec_write);
}
-static void handle_response_confirmation(void *buf, uint16_t len)
+static void handle_response_confirmation(void *buf, uint16_t len, int fd)
{
struct hal_ev_gatt_server_rsp_confirmation *ev = buf;
@@ -573,6 +573,9 @@
{
struct hal_cmd_gatt_client_register cmd;
+ if (!interface_ready())
+ return BT_STATUS_NOT_READY;
+
memcpy(cmd.uuid, uuid, sizeof(*uuid));
return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_REGISTER,
@@ -968,19 +971,28 @@
return dev_type;
}
-static bt_status_t set_adv_data(int server_if, bool set_scan_rsp,
+static bt_status_t set_adv_data_real(int server_if, bool set_scan_rsp,
bool include_name, bool include_txpower,
int min_interval, int max_interval,
int appearance, uint16_t manufacturer_len,
- char *manufacturer_data)
+ char *manufacturer_data,
+ uint16_t service_data_len, char *service_data,
+ uint16_t service_uuid_len, char *service_uuid)
{
char buf[IPC_MTU];
struct hal_cmd_gatt_client_set_adv_data *cmd = (void *) buf;
- size_t cmd_len = sizeof(*cmd) + manufacturer_len;
+ size_t cmd_len;
+ uint8_t *data;
if (!interface_ready())
return BT_STATUS_NOT_READY;
+ cmd_len = sizeof(*cmd) + manufacturer_len + service_data_len +
+ service_uuid_len;
+
+ if (cmd_len > IPC_MTU)
+ return BT_STATUS_FAIL;
+
cmd->server_if = server_if;
cmd->set_scan_rsp = set_scan_rsp;
cmd->include_name = include_name;
@@ -989,13 +1001,65 @@
cmd->max_interval = max_interval;
cmd->appearance = appearance;
cmd->manufacturer_len = manufacturer_len;
+ cmd->service_data_len = service_data_len;
+ cmd->service_uuid_len = service_uuid_len;
- memcpy(cmd->manufacturer_data, manufacturer_data, manufacturer_len);
+ data = cmd->data;
+
+ if (manufacturer_data && manufacturer_len) {
+ memcpy(data, manufacturer_data, manufacturer_len);
+ data += manufacturer_len;
+ }
+
+ if (service_data && service_data_len) {
+ memcpy(data, service_data, service_data_len);
+ data += service_data_len;
+ }
+
+ if (service_uuid && service_uuid_len) {
+ memcpy(data, service_uuid, service_uuid_len);
+ data += service_uuid_len;
+ }
return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_SET_ADV_DATA,
cmd_len, cmd, 0, NULL, NULL);
}
+/*
+ * This is temporary solution and support for older Android versions might
+ * be removed at any time.
+ */
+#if ANDROID_VERSION < PLATFORM_VER(4,4,3)
+static bt_status_t set_adv_data(int server_if, bool set_scan_rsp,
+ bool include_name, bool include_txpower,
+ int min_interval, int max_interval,
+ int appearance, uint16_t manufacturer_len,
+ char *manufacturer_data)
+{
+ return set_adv_data_real(server_if, set_scan_rsp, include_name,
+ include_txpower, min_interval,
+ max_interval, appearance,
+ manufacturer_len, manufacturer_data,
+ 0, NULL, 0, NULL);
+}
+#else
+static bt_status_t set_adv_data(int server_if, bool set_scan_rsp,
+ bool include_name, bool include_txpower,
+ int min_interval, int max_interval,
+ int appearance, uint16_t manufacturer_len,
+ char *manufacturer_data,
+ uint16_t service_data_len, char *service_data,
+ uint16_t service_uuid_len, char *service_uuid)
+{
+ return set_adv_data_real(server_if, set_scan_rsp, include_name,
+ include_txpower, min_interval,
+ max_interval, appearance,
+ manufacturer_len, manufacturer_data,
+ service_data_len, service_data,
+ service_uuid_len, service_uuid);
+}
+#endif
+
static bt_status_t test_command(int command, btgatt_test_params_t *params)
{
struct hal_cmd_gatt_client_test_command cmd;
diff --git a/android/hal-handsfree.c b/android/hal-handsfree.c
index d992506..1d9a1a2 100644
--- a/android/hal-handsfree.c
+++ b/android/hal-handsfree.c
@@ -37,7 +37,7 @@
return cbs != NULL;
}
-static void handle_conn_state(void *buf, uint16_t len)
+static void handle_conn_state(void *buf, uint16_t len, int fd)
{
struct hal_ev_handsfree_conn_state *ev = buf;
@@ -46,7 +46,7 @@
(bt_bdaddr_t *) (ev->bdaddr));
}
-static void handle_audio_state(void *buf, uint16_t len)
+static void handle_audio_state(void *buf, uint16_t len, int fd)
{
struct hal_ev_handsfree_audio_state *ev = buf;
@@ -54,7 +54,7 @@
cbs->audio_state_cb(ev->state, (bt_bdaddr_t *) (ev->bdaddr));
}
-static void handle_vr_state(void *buf, uint16_t len)
+static void handle_vr_state(void *buf, uint16_t len, int fd)
{
struct hal_ev_handsfree_vr_state *ev = buf;
@@ -62,19 +62,19 @@
cbs->vr_cmd_cb(ev->state);
}
-static void handle_answer(void *buf, uint16_t len)
+static void handle_answer(void *buf, uint16_t len, int fd)
{
if (cbs->answer_call_cmd_cb)
cbs->answer_call_cmd_cb();
}
-static void handle_hangup(void *buf, uint16_t len)
+static void handle_hangup(void *buf, uint16_t len, int fd)
{
if (cbs->hangup_call_cmd_cb)
cbs->hangup_call_cmd_cb();
}
-static void handle_volume(void *buf, uint16_t len)
+static void handle_volume(void *buf, uint16_t len, int fd)
{
struct hal_ev_handsfree_volume *ev = buf;
@@ -82,7 +82,7 @@
cbs->volume_cmd_cb(ev->type, ev->volume);
}
-static void handle_dial(void *buf, uint16_t len)
+static void handle_dial(void *buf, uint16_t len, int fd)
{
struct hal_ev_handsfree_dial *ev = buf;
uint16_t num_len = ev->number_len;
@@ -102,7 +102,7 @@
cbs->dial_call_cmd_cb(NULL);
}
-static void handle_dtmf(void *buf, uint16_t len)
+static void handle_dtmf(void *buf, uint16_t len, int fd)
{
struct hal_ev_handsfree_dtmf *ev = buf;
@@ -110,7 +110,7 @@
cbs->dtmf_cmd_cb(ev->tone);
}
-static void handle_nrec(void *buf, uint16_t len)
+static void handle_nrec(void *buf, uint16_t len, int fd)
{
struct hal_ev_handsfree_nrec *ev = buf;
@@ -118,7 +118,7 @@
cbs->nrec_cmd_cb(ev->nrec);
}
-static void handle_chld(void *buf, uint16_t len)
+static void handle_chld(void *buf, uint16_t len, int fd)
{
struct hal_ev_handsfree_chld *ev = buf;
@@ -126,31 +126,31 @@
cbs->chld_cmd_cb(ev->chld);
}
-static void handle_cnum(void *buf, uint16_t len)
+static void handle_cnum(void *buf, uint16_t len, int fd)
{
if (cbs->cnum_cmd_cb)
cbs->cnum_cmd_cb();
}
-static void handle_cind(void *buf, uint16_t len)
+static void handle_cind(void *buf, uint16_t len, int fd)
{
if (cbs->cind_cmd_cb)
cbs->cind_cmd_cb();
}
-static void handle_cops(void *buf, uint16_t len)
+static void handle_cops(void *buf, uint16_t len, int fd)
{
if (cbs->cops_cmd_cb)
cbs->cops_cmd_cb();
}
-static void handle_clcc(void *buf, uint16_t len)
+static void handle_clcc(void *buf, uint16_t len, int fd)
{
if (cbs->clcc_cmd_cb)
cbs->clcc_cmd_cb();
}
-static void handle_unknown_at(void *buf, uint16_t len)
+static void handle_unknown_at(void *buf, uint16_t len, int fd)
{
struct hal_ev_handsfree_unknown_at *ev = buf;
@@ -164,7 +164,7 @@
cbs->unknown_at_cmd_cb((char *) ev->buf);
}
-static void handle_hsp_key_press(void *buf, uint16_t len)
+static void handle_hsp_key_press(void *buf, uint16_t len, int fd)
{
if (cbs->key_pressed_cmd_cb)
cbs->key_pressed_cmd_cb();
diff --git a/android/hal-health.c b/android/hal-health.c
index 427d4c9..ac6e87f 100644
--- a/android/hal-health.c
+++ b/android/hal-health.c
@@ -33,18 +33,41 @@
return cbacks != NULL;
}
+static void handle_app_registration_state(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_health_app_reg_state *ev = buf;
+
+ if (cbacks->app_reg_state_cb)
+ cbacks->app_reg_state_cb(ev->id, ev->state);
+}
+
+static void handle_channel_state(void *buf, uint16_t len, int fd)
+{
+ struct hal_ev_health_channel_state *ev = buf;
+
+ if (cbacks->channel_state_cb)
+ cbacks->channel_state_cb(ev->app_id, (bt_bdaddr_t *) ev->bdaddr,
+ ev->mdep_index, ev->channel_id,
+ ev->channel_state, fd);
+}
+
/*
* handlers will be called from notification thread context,
* index in table equals to 'opcode - HAL_MINIMUM_EVENT'
*/
static const struct hal_ipc_handler ev_handlers[] = {
+ /* HAL_EV_HEALTH_APP_REG_STATE */
+ { handle_app_registration_state, false,
+ sizeof(struct hal_ev_health_app_reg_state) },
+ /* HAL_EV_HEALTH_CHANNEL_STATE */
+ { handle_channel_state, false,
+ sizeof(struct hal_ev_health_channel_state) },
};
static bt_status_t register_application(bthl_reg_param_t *reg, int *app_id)
{
uint8_t buf[IPC_MTU];
struct hal_cmd_health_reg_app *cmd = (void *) buf;
- struct hal_cmd_health_mdep *mdep = (void *) buf;
struct hal_rsp_health_reg_app rsp;
size_t rsp_len = sizeof(rsp);
bt_status_t status;
@@ -69,37 +92,40 @@
memcpy(cmd->data, reg->application_name, len);
off += len;
+ cmd->provider_name_off = off;
if (reg->provider_name) {
len = strlen(reg->provider_name) + 1;
- cmd->provider_name_off = off;
memcpy(cmd->data + off, reg->provider_name, len);
off += len;
}
+ cmd->service_name_off = off;
if (reg->srv_name) {
len = strlen(reg->srv_name) + 1;
- cmd->service_name_off = off;
memcpy(cmd->data + off, reg->srv_name, len);
off += len;
}
+ cmd->service_descr_off = off;
if (reg->srv_desp) {
len = strlen(reg->srv_desp) + 1;
- cmd->service_descr_off = off;
memcpy(cmd->data + off, reg->srv_desp, len);
off += len;
}
cmd->len = off;
status = hal_ipc_cmd(HAL_SERVICE_ID_HEALTH, HAL_OP_HEALTH_REG_APP,
- sizeof(*cmd) + cmd->len, &cmd,
+ sizeof(*cmd) + cmd->len, buf,
&rsp_len, &rsp, NULL);
if (status != BT_STATUS_SUCCESS)
return status;
for (i = 0; i < reg->number_of_mdeps; i++) {
+ struct hal_cmd_health_mdep *mdep = (void *) buf;
+
memset(buf, 0, IPC_MTU);
+ mdep->app_id = rsp.app_id;
mdep->role = reg->mdep_cfg[i].mdep_role;
mdep->data_type = reg->mdep_cfg[i].data_type;
mdep->channel_type = reg->mdep_cfg[i].channel_type;
@@ -117,7 +143,6 @@
if (status != BT_STATUS_SUCCESS)
return status;
-
}
*app_id = rsp.app_id;
diff --git a/android/hal-hidhost.c b/android/hal-hidhost.c
index 5787b5e..949e2a0 100644
--- a/android/hal-hidhost.c
+++ b/android/hal-hidhost.c
@@ -33,7 +33,7 @@
return cbacks != NULL;
}
-static void handle_conn_state(void *buf, uint16_t len)
+static void handle_conn_state(void *buf, uint16_t len, int fd)
{
struct hal_ev_hidhost_conn_state *ev = buf;
@@ -42,7 +42,7 @@
ev->state);
}
-static void handle_info(void *buf, uint16_t len)
+static void handle_info(void *buf, uint16_t len, int fd)
{
struct hal_ev_hidhost_info *ev = buf;
bthh_hid_info_t info;
@@ -61,7 +61,7 @@
cbacks->hid_info_cb((bt_bdaddr_t *) ev->bdaddr, info);
}
-static void handle_proto_mode(void *buf, uint16_t len)
+static void handle_proto_mode(void *buf, uint16_t len, int fd)
{
struct hal_ev_hidhost_proto_mode *ev = buf;
@@ -70,7 +70,7 @@
ev->status, ev->mode);
}
-static void handle_idle_time(void *buf, uint16_t len)
+static void handle_idle_time(void *buf, uint16_t len, int fd)
{
struct hal_ev_hidhost_idle_time *ev = buf;
@@ -79,7 +79,7 @@
ev->idle_rate);
}
-static void handle_get_report(void *buf, uint16_t len)
+static void handle_get_report(void *buf, uint16_t len, int fd)
{
struct hal_ev_hidhost_get_report *ev = buf;
@@ -93,7 +93,7 @@
ev->data, ev->len);
}
-static void handle_virtual_unplug(void *buf, uint16_t len)
+static void handle_virtual_unplug(void *buf, uint16_t len, int fd)
{
struct hal_ev_hidhost_virtual_unplug *ev = buf;
@@ -107,36 +107,19 @@
* index in table equals to 'opcode - HAL_MINIMUM_EVENT'
*/
static const struct hal_ipc_handler ev_handlers[] = {
- { /* HAL_EV_HIDHOST_CONN_STATE */
- .handler = handle_conn_state,
- .var_len = false,
- .data_len = sizeof(struct hal_ev_hidhost_conn_state)
- },
- { /* HAL_EV_HIDHOST_INFO */
- .handler = handle_info,
- .var_len = false,
- .data_len = sizeof(struct hal_ev_hidhost_info),
- },
- { /* HAL_EV_HIDHOST_PROTO_MODE */
- .handler = handle_proto_mode,
- .var_len = false,
- .data_len = sizeof(struct hal_ev_hidhost_proto_mode),
- },
- { /* HAL_EV_HIDHOST_IDLE_TIME */
- .handler = handle_idle_time,
- .var_len = false,
- .data_len = sizeof(struct hal_ev_hidhost_idle_time),
- },
- { /* HAL_EV_HIDHOST_GET_REPORT */
- .handler = handle_get_report,
- .var_len = true,
- .data_len = sizeof(struct hal_ev_hidhost_get_report),
- },
- { /* HAL_EV_HIDHOST_VIRTUAL_UNPLUG */
- .handler = handle_virtual_unplug,
- .var_len = false,
- .data_len = sizeof(struct hal_ev_hidhost_virtual_unplug),
- },
+ /* HAL_EV_HIDHOST_CONN_STATE */
+ { handle_conn_state, false, sizeof(struct hal_ev_hidhost_conn_state) },
+ /* HAL_EV_HIDHOST_INFO */
+ { handle_info, false, sizeof(struct hal_ev_hidhost_info) },
+ /* HAL_EV_HIDHOST_PROTO_MODE */
+ { handle_proto_mode, false, sizeof(struct hal_ev_hidhost_proto_mode) },
+ /* HAL_EV_HIDHOST_IDLE_TIME */
+ { handle_idle_time, false, sizeof(struct hal_ev_hidhost_idle_time) },
+ /* HAL_EV_HIDHOST_GET_REPORT */
+ { handle_get_report, true, sizeof(struct hal_ev_hidhost_get_report) },
+ /* HAL_EV_HIDHOST_VIRTUAL_UNPLUG */
+ { handle_virtual_unplug, false,
+ sizeof(struct hal_ev_hidhost_virtual_unplug) },
};
static bt_status_t hidhost_connect(bt_bdaddr_t *bd_addr)
diff --git a/android/hal-ipc-api.txt b/android/hal-ipc-api.txt
index 43e2709..ae62c10 100644
--- a/android/hal-ipc-api.txt
+++ b/android/hal-ipc-api.txt
@@ -1143,8 +1143,9 @@
Opcode 0x02 - Register Application MDEP data command/response
- Command parameters: MDEP Role (1 octet)
- Data type (1 octet)
+ Command parameters: Application ID (2 octets)
+ MDEP Role (1 octet)
+ Data type (2 octets)
Channel type (1 octet)
MDEP description length (2 octets)
MDEP description (MDEP desciption length)
diff --git a/android/hal-ipc.c b/android/hal-ipc.c
index 8f5babe..494ba85 100644
--- a/android/hal-ipc.c
+++ b/android/hal-ipc.c
@@ -35,6 +35,7 @@
#define CONNECT_TIMEOUT (10 * 1000)
+static int listen_sk = -1;
static int cmd_sk = -1;
static int notif_sk = -1;
@@ -62,41 +63,39 @@
services[service].size = 0;
}
-static void handle_msg(void *buf, ssize_t len)
+static bool handle_msg(void *buf, ssize_t len, int fd)
{
struct ipc_hdr *msg = buf;
const struct hal_ipc_handler *handler;
uint8_t opcode;
if (len < (ssize_t) sizeof(*msg)) {
- error("IPC: message too small (%zd bytes), aborting", len);
- exit(EXIT_FAILURE);
+ error("IPC: message too small (%zd bytes)", len);
+ return false;
}
if (len != (ssize_t) (sizeof(*msg) + msg->len)) {
- error("IPC: message malformed (%zd bytes), aborting", len);
- exit(EXIT_FAILURE);
+ error("IPC: message malformed (%zd bytes)", len);
+ return false;
}
/* if service is valid */
if (msg->service_id > HAL_SERVICE_ID_MAX) {
- error("IPC: unknown service (0x%x), aborting",
- msg->service_id);
- exit(EXIT_FAILURE);
+ error("IPC: unknown service (0x%x)", msg->service_id);
+ return false;
}
/* if service is registered */
if (!services[msg->service_id].handler) {
- error("IPC: unregistered service (0x%x), aborting",
- msg->service_id);
- exit(EXIT_FAILURE);
+ error("IPC: unregistered service (0x%x)", msg->service_id);
+ return false;
}
/* if opcode fit valid range */
if (msg->opcode < HAL_MINIMUM_EVENT) {
- error("IPC: invalid opcode for service 0x%x (0x%x), aborting",
+ error("IPC: invalid opcode for service 0x%x (0x%x)",
msg->service_id, msg->opcode);
- exit(EXIT_FAILURE);
+ return false;
}
/*
@@ -107,9 +106,9 @@
/* if opcode is valid */
if (opcode >= services[msg->service_id].size) {
- error("IPC: invalid opcode for service 0x%x (0x%x), aborting",
+ error("IPC: invalid opcode for service 0x%x (0x%x)",
msg->service_id, msg->opcode);
- exit(EXIT_FAILURE);
+ return false;
}
handler = &services[msg->service_id].handler[opcode];
@@ -118,12 +117,14 @@
if ((handler->var_len && handler->data_len > msg->len) ||
(!handler->var_len && handler->data_len != msg->len)) {
error("IPC: message size invalid for service 0x%x opcode 0x%x "
- "(%u bytes), aborting",
+ "(%u bytes)",
msg->service_id, msg->opcode, msg->len);
- exit(EXIT_FAILURE);
+ return false;
}
- handler->handler(msg->payload, msg->len);
+ handler->handler(msg->payload, msg->len, fd);
+
+ return true;
}
static void *notification_handler(void *data)
@@ -154,9 +155,9 @@
ret = recvmsg(notif_sk, &msg, 0);
if (ret < 0) {
- error("Receiving notifications failed, aborting :%s",
+ error("Receiving notifications failed: %s",
strerror(errno));
- exit(EXIT_FAILURE);
+ goto failed;
}
/* socket was shutdown */
@@ -168,8 +169,8 @@
}
pthread_mutex_unlock(&cmd_sk_mutex);
- error("Notification socket closed, aborting");
- exit(EXIT_FAILURE);
+ error("Notification socket closed");
+ goto failed;
}
fd = -1;
@@ -184,7 +185,8 @@
}
}
- handle_msg(buf, ret);
+ if (!handle_msg(buf, ret, fd))
+ goto failed;
}
close(notif_sk);
@@ -195,6 +197,9 @@
DBG("exit");
return NULL;
+
+failed:
+ exit(EXIT_FAILURE);
}
static int accept_connection(int sk)
@@ -229,7 +234,39 @@
return new_sk;
}
-bool hal_ipc_init(void)
+bool hal_ipc_accept(void)
+{
+ int err;
+
+ cmd_sk = accept_connection(listen_sk);
+ if (cmd_sk < 0)
+ return false;
+
+ notif_sk = accept_connection(listen_sk);
+ if (notif_sk < 0) {
+ close(cmd_sk);
+ cmd_sk = -1;
+ return false;
+ }
+
+ err = pthread_create(¬if_th, NULL, notification_handler, NULL);
+ if (err) {
+ notif_th = 0;
+ error("Failed to start notification thread: %d (%s)", err,
+ strerror(err));
+ close(cmd_sk);
+ cmd_sk = -1;
+ close(notif_sk);
+ notif_sk = -1;
+ return false;
+ }
+
+ info("IPC connected");
+
+ return true;
+}
+
+bool hal_ipc_init(const char *path, size_t size)
{
struct sockaddr_un addr;
int sk;
@@ -246,7 +283,7 @@
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
- memcpy(addr.sun_path, BLUEZ_HAL_SK_PATH, sizeof(BLUEZ_HAL_SK_PATH));
+ memcpy(addr.sun_path, path, size);
if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
err = errno;
@@ -263,53 +300,26 @@
return false;
}
- /* Start Android Bluetooth daemon service */
- if (property_set("bluetooth.start", "daemon") < 0) {
- error("Failed to set bluetooth.start=daemon");
- close(sk);
- return false;
- }
-
- cmd_sk = accept_connection(sk);
- if (cmd_sk < 0) {
- close(sk);
- return false;
- }
-
- notif_sk = accept_connection(sk);
- if (notif_sk < 0) {
- close(sk);
- close(cmd_sk);
- cmd_sk = -1;
- return false;
- }
-
- info("bluetoothd connected");
-
- close(sk);
-
- err = pthread_create(¬if_th, NULL, notification_handler, NULL);
- if (err) {
- notif_th = 0;
- error("Failed to start notification thread: %d (%s)", err,
- strerror(err));
- close(cmd_sk);
- cmd_sk = -1;
- close(notif_sk);
- notif_sk = -1;
- return false;
- }
+ listen_sk = sk;
return true;
}
void hal_ipc_cleanup(void)
{
+ close(listen_sk);
+ listen_sk = -1;
+
pthread_mutex_lock(&cmd_sk_mutex);
- close(cmd_sk);
- cmd_sk = -1;
+ if (cmd_sk >= 0) {
+ close(cmd_sk);
+ cmd_sk = -1;
+ }
pthread_mutex_unlock(&cmd_sk_mutex);
+ if (notif_sk < 0)
+ return;
+
shutdown(notif_sk, SHUT_RD);
pthread_join(notif_th, NULL);
@@ -328,8 +338,8 @@
size_t s_len = sizeof(s);
if (cmd_sk < 0) {
- error("Invalid cmd socket passed to hal_ipc_cmd, aborting");
- exit(EXIT_FAILURE);
+ error("Invalid cmd socket passed to hal_ipc_cmd");
+ goto failed;
}
if (!rsp || !rsp_len) {
@@ -358,15 +368,16 @@
ret = sendmsg(cmd_sk, &msg, 0);
if (ret < 0) {
- error("Sending command failed, aborting :%s", strerror(errno));
+ error("Sending command failed:%s", strerror(errno));
pthread_mutex_unlock(&cmd_sk_mutex);
- exit(EXIT_FAILURE);
+ goto failed;
}
/* socket was shutdown */
if (ret == 0) {
- error("Command socket closed, aborting");
- exit(EXIT_FAILURE);
+ error("Command socket closed");
+ pthread_mutex_unlock(&cmd_sk_mutex);
+ goto failed;
}
memset(&msg, 0, sizeof(msg));
@@ -388,48 +399,48 @@
}
ret = recvmsg(cmd_sk, &msg, 0);
- if (ret < 0) {
- error("Receiving command response failed, aborting :%s",
- strerror(errno));
- pthread_mutex_unlock(&cmd_sk_mutex);
- exit(EXIT_FAILURE);
- }
pthread_mutex_unlock(&cmd_sk_mutex);
+ if (ret < 0) {
+ error("Receiving command response failed: %s", strerror(errno));
+ goto failed;
+ }
+
+
if (ret < (ssize_t) sizeof(cmd)) {
- error("Too small response received(%zd bytes), aborting", ret);
- exit(EXIT_FAILURE);
+ error("Too small response received(%zd bytes)", ret);
+ goto failed;
}
if (cmd.service_id != service_id) {
- error("Invalid service id (0x%x vs 0x%x), aborting",
+ error("Invalid service id (0x%x vs 0x%x)",
cmd.service_id, service_id);
- exit(EXIT_FAILURE);
+ goto failed;
}
if (ret != (ssize_t) (sizeof(cmd) + cmd.len)) {
- error("Malformed response received(%zd bytes), aborting", ret);
- exit(EXIT_FAILURE);
+ error("Malformed response received(%zd bytes)", ret);
+ goto failed;
}
if (cmd.opcode != opcode && cmd.opcode != HAL_OP_STATUS) {
- error("Invalid opcode received (0x%x vs 0x%x), aborting",
+ error("Invalid opcode received (0x%x vs 0x%x)",
cmd.opcode, opcode);
- exit(EXIT_FAILURE);
+ goto failed;
}
if (cmd.opcode == HAL_OP_STATUS) {
struct ipc_status *s = rsp;
if (sizeof(*s) != cmd.len) {
- error("Invalid status length, aborting");
- exit(EXIT_FAILURE);
+ error("Invalid status length");
+ goto failed;
}
if (s->code == HAL_STATUS_SUCCESS) {
- error("Invalid success status response, aborting");
- exit(EXIT_FAILURE);
+ error("Invalid success status response");
+ goto failed;
}
return s->code;
@@ -455,4 +466,7 @@
*rsp_len = cmd.len;
return BT_STATUS_SUCCESS;
+
+failed:
+ exit(EXIT_FAILURE);
}
diff --git a/android/hal-ipc.h b/android/hal-ipc.h
index 2fbf30f..08ed7cc 100644
--- a/android/hal-ipc.h
+++ b/android/hal-ipc.h
@@ -16,12 +16,13 @@
*/
struct hal_ipc_handler {
- void (*handler) (void *buf, uint16_t len);
+ void (*handler) (void *buf, uint16_t len, int fd);
bool var_len;
size_t data_len;
};
-bool hal_ipc_init(void);
+bool hal_ipc_init(const char *path, size_t size);
+bool hal_ipc_accept(void);
void hal_ipc_cleanup(void);
int hal_ipc_cmd(uint8_t service_id, uint8_t opcode, uint16_t len, void *param,
diff --git a/android/hal-msg.h b/android/hal-msg.h
index 09bd9a0..af859c6 100644
--- a/android/hal-msg.h
+++ b/android/hal-msg.h
@@ -405,8 +405,9 @@
#define HAL_OP_HEALTH_MDEP 0x02
struct hal_cmd_health_mdep {
+ uint16_t app_id;
uint8_t role;
- uint8_t data_type;
+ uint16_t data_type;
uint8_t channel_type;
uint16_t descr_len;
uint8_t descr[0];
@@ -744,9 +745,20 @@
int32_t max_interval;
int32_t appearance;
uint16_t manufacturer_len;
- uint8_t manufacturer_data[0];
+ uint16_t service_data_len;
+ uint16_t service_uuid_len;
+ uint8_t data[0];
} __attribute__((packed));
+#define GATT_CLIENT_TEST_CMD_ENABLE 0x01
+#define GATT_CLIENT_TEST_CMD_CONNECT 0x02
+#define GATT_CLIENT_TEST_CMD_DISCONNECT 0x03
+#define GATT_CLIENT_TEST_CMD_DISCOVER 0x04
+#define GATT_CLIENT_TEST_CMD_READ 0xe0
+#define GATT_CLIENT_TEST_CMD_WRITE 0xe1
+#define GATT_CLIENT_TEST_CMD_INCREASE_SECURITY 0xe2
+#define GATT_CLIENT_TEST_CMD_PAIRING_CONFIG 0xf0
+
#define HAL_OP_GATT_CLIENT_TEST_COMMAND 0x16
struct hal_cmd_gatt_client_test_command {
int32_t command;
@@ -1590,3 +1602,16 @@
int32_t status;
int32_t handle;
} __attribute__((packed));
+
+#define HAL_GATT_PERMISSION_READ 0x0001
+#define HAL_GATT_PERMISSION_READ_ENCRYPTED 0x0002
+#define HAL_GATT_PERMISSION_READ_ENCRYPTED_MITM 0x0004
+#define HAL_GATT_PERMISSION_WRITE 0x0010
+#define HAL_GATT_PERMISSION_WRITE_ENCRYPTED 0x0020
+#define HAL_GATT_PERMISSION_WRITE_ENCRYPTED_MITM 0x0040
+#define HAL_GATT_PERMISSION_WRITE_SIGNED 0x0080
+#define HAL_GATT_PERMISSION_WRITE_SIGNED_MITM 0x0100
+
+#define HAL_GATT_AUTHENTICATION_NONE 0
+#define HAL_GATT_AUTHENTICATION_NO_MITM 1
+#define HAL_GATT_AUTHENTICATION_MITM 2
diff --git a/android/hal-pan.c b/android/hal-pan.c
index a9ac4ff..d3f93ff 100644
--- a/android/hal-pan.c
+++ b/android/hal-pan.c
@@ -31,7 +31,7 @@
return cbs != NULL;
}
-static void handle_conn_state(void *buf, uint16_t len)
+static void handle_conn_state(void *buf, uint16_t len, int fd)
{
struct hal_ev_pan_conn_state *ev = buf;
@@ -41,7 +41,7 @@
ev->local_role, ev->remote_role);
}
-static void handle_ctrl_state(void *buf, uint16_t len)
+static void handle_ctrl_state(void *buf, uint16_t len, int fd)
{
struct hal_ev_pan_ctrl_state *ev = buf;
@@ -65,16 +65,10 @@
* index in table equals to 'opcode - HAL_MINIMUM_EVENT'
*/
static const struct hal_ipc_handler ev_handlers[] = {
- { /* HAL_EV_PAN_CTRL_STATE */
- .handler = handle_ctrl_state,
- .var_len = false,
- .data_len = sizeof(struct hal_ev_pan_ctrl_state),
- },
- { /* HAL_EV_PAN_CONN_STATE */
- .handler = handle_conn_state,
- .var_len = false,
- .data_len = sizeof(struct hal_ev_pan_conn_state),
- },
+ /* HAL_EV_PAN_CTRL_STATE */
+ { handle_ctrl_state, false, sizeof(struct hal_ev_pan_ctrl_state) },
+ /* HAL_EV_PAN_CONN_STATE */
+ { handle_conn_state, false, sizeof(struct hal_ev_pan_conn_state) },
};
static bt_status_t pan_enable(int local_role)
diff --git a/android/hal-sco.c b/android/hal-sco.c
index ea9857e..5888275 100644
--- a/android/hal-sco.c
+++ b/android/hal-sco.c
@@ -617,6 +617,9 @@
return 0;
failed:
+ if (out->resampler)
+ release_resampler(out->resampler);
+
free(out->downmix_buf);
free(out);
stream_out = NULL;
@@ -638,6 +641,9 @@
sco_dev->out->fd = -1;
}
+ if (out->resampler)
+ release_resampler(out->resampler);
+
free(out->downmix_buf);
free(out);
sco_dev->out = NULL;
diff --git a/android/hal-utils.h b/android/hal-utils.h
index 8a1d015..a0aab57 100644
--- a/android/hal-utils.h
+++ b/android/hal-utils.h
@@ -17,6 +17,8 @@
#include <hardware/bluetooth.h>
+#define PLATFORM_VER(a,b,c) ((a << 16) | ( b << 8) | (c))
+
#define MAX_UUID_STR_LEN 37
#define HAL_UUID_LEN 16
#define MAX_ADDR_STR_LEN 18
diff --git a/android/hardware/bt_gatt_client.h b/android/hardware/bt_gatt_client.h
index d6b0cb4..cf4fabe 100644
--- a/android/hardware/bt_gatt_client.h
+++ b/android/hardware/bt_gatt_client.h
@@ -278,7 +278,9 @@
/** Set the advertising data or scan response data */
bt_status_t (*set_adv_data)(int server_if, bool set_scan_rsp, bool include_name,
bool include_txpower, int min_interval, int max_interval, int appearance,
- uint16_t manufacturer_len, char* manufacturer_data);
+ uint16_t manufacturer_len, char* manufacturer_data,
+ uint16_t service_data_len, char* service_data,
+ uint16_t service_uuid_len, char* service_uuid);
/** Test mode interface */
bt_status_t (*test_command)( int command, btgatt_test_params_t* params);
diff --git a/android/health.c b/android/health.c
index 655d9f9..0462e99 100644
--- a/android/health.c
+++ b/android/health.c
@@ -3,6 +3,7 @@
* BlueZ - Bluetooth protocol stack for Linux
*
* Copyright (C) 2014 Intel Corporation. All rights reserved.
+ * Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos.
*
*
* This library is free software; you can redistribute it and/or
@@ -31,10 +32,17 @@
#include <unistd.h>
#include <glib.h>
+#include "btio/btio.h"
#include "lib/bluetooth.h"
#include "lib/sdp.h"
#include "lib/sdp_lib.h"
+#include "lib/uuid.h"
+#include "lib/l2cap.h"
#include "src/log.h"
+#include "src/shared/util.h"
+#include "src/shared/queue.h"
+#include "src/uuid-helper.h"
+#include "src/sdp-client.h"
#include "hal-msg.h"
#include "ipc-common.h"
@@ -42,40 +50,1152 @@
#include "utils.h"
#include "bluetooth.h"
#include "health.h"
+#include "mcap-lib.h"
+
+#define SVC_HINT_HEALTH 0x00
+#define HDP_VERSION 0x0101
+#define DATA_EXCHANGE_SPEC_11073 0x01
+
+#define CHANNEL_TYPE_ANY 0x00
+#define CHANNEL_TYPE_RELIABLE 0x01
+#define CHANNEL_TYPE_STREAM 0x02
static bdaddr_t adapter_addr;
static struct ipc *hal_ipc = NULL;
+static struct queue *apps = NULL;
+static struct mcap_instance *mcap = NULL;
+static uint32_t record_id = 0;
+static uint32_t record_state = 0;
+
+struct mdep_cfg {
+ uint8_t role;
+ uint16_t data_type;
+ uint8_t channel_type;
+ char *descr;
+
+ uint8_t id; /* mdep id */
+};
+
+struct health_device {
+ bdaddr_t dst;
+ uint16_t app_id;
+
+ struct mcap_mcl *mcl;
+ bool mcl_conn;
+
+ struct queue *channels; /* data channels */
+
+ uint16_t ccpsm;
+ uint16_t dcpsm;
+};
+
+struct health_channel {
+ uint8_t mdep_id;
+ uint8_t type;
+
+ struct health_device *dev;
+
+ uint16_t id; /* channel id */
+};
+
+struct health_app {
+ char *app_name;
+ char *provider_name;
+ char *service_name;
+ char *service_descr;
+ uint8_t num_of_mdep;
+ struct queue *mdeps;
+
+ uint16_t id; /* app id */
+ struct queue *devices;
+};
+
+static void free_health_channel(void *data)
+{
+ struct health_channel *channel = data;
+
+ if (!channel)
+ return;
+
+ free(channel);
+}
+
+static void destroy_channel(void *data)
+{
+ struct health_channel *channel = data;
+
+ if (!channel)
+ return;
+
+ /* TODO: Notify channel connection status DESTROYED */
+ queue_remove(channel->dev->channels, channel);
+ free_health_channel(channel);
+}
+
+static void unref_mcl(struct health_device *dev)
+{
+ if (!dev || !dev->mcl)
+ return;
+
+ mcap_close_mcl(dev->mcl, FALSE);
+ mcap_mcl_unref(dev->mcl);
+ dev->mcl = NULL;
+ dev->mcl_conn = false;
+}
+
+static void free_health_device(void *data)
+{
+ struct health_device *dev = data;
+
+ if (!dev)
+ return;
+
+ unref_mcl(dev);
+ queue_destroy(dev->channels, free_health_channel);
+ free(dev);
+}
+
+static void free_mdep_cfg(void *data)
+{
+ struct mdep_cfg *cfg = data;
+
+ if (!cfg)
+ return;
+
+ free(cfg->descr);
+ free(cfg);
+}
+
+static void free_health_app(void *data)
+{
+ struct health_app *app = data;
+
+ if (!app)
+ return;
+
+ free(app->app_name);
+ free(app->provider_name);
+ free(app->service_name);
+ free(app->service_descr);
+ queue_destroy(app->mdeps, free_mdep_cfg);
+ queue_destroy(app->devices, free_health_device);
+ free(app);
+}
+
+static void send_app_reg_notify(struct health_app *app, uint8_t state)
+{
+ struct hal_ev_health_app_reg_state ev;
+
+ DBG("");
+
+ ev.id = app->id;
+ ev.state = state;
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HEALTH,
+ HAL_EV_HEALTH_APP_REG_STATE, sizeof(ev), &ev);
+}
+
+static void send_channel_state_notify(struct health_channel *channel,
+ uint8_t state, int fd)
+{
+ struct hal_ev_health_channel_state ev;
+
+ DBG("");
+
+ bdaddr2android(&channel->dev->dst, ev.bdaddr);
+ ev.app_id = channel->dev->app_id;
+ ev.mdep_index = channel->mdep_id;
+ ev.channel_id = channel->id;
+ ev.channel_state = state;
+
+ ipc_send_notif_with_fd(hal_ipc, HAL_SERVICE_ID_HEALTH,
+ HAL_EV_HEALTH_CHANNEL_STATE,
+ sizeof(ev), &ev, fd);
+}
+
+static bool mdep_by_mdep_role(const void *data, const void *user_data)
+{
+ const struct mdep_cfg *mdep = data;
+ uint16_t role = PTR_TO_INT(user_data);
+
+ return mdep->role == role;
+}
+
+static bool mdep_by_mdep_id(const void *data, const void *user_data)
+{
+ const struct mdep_cfg *mdep = data;
+ uint16_t mdep_id = PTR_TO_INT(user_data);
+
+ return mdep->id == mdep_id;
+}
+
+static bool app_by_app_id(const void *data, const void *user_data)
+{
+ const struct health_app *app = data;
+ uint16_t app_id = PTR_TO_INT(user_data);
+
+ return app->id == app_id;
+}
+
+static int register_service_protocols(sdp_record_t *rec,
+ struct health_app *app)
+{
+ uuid_t l2cap_uuid, mcap_c_uuid;
+ sdp_list_t *l2cap_list, *proto_list = NULL, *mcap_list = NULL;
+ sdp_list_t *access_proto_list = NULL;
+ sdp_data_t *psm = NULL, *mcap_ver = NULL;
+ uint32_t ccpsm;
+ uint16_t version = MCAP_VERSION;
+ GError *err = NULL;
+ int ret = -1;
+
+ DBG("");
+
+ /* set l2cap information */
+ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+ l2cap_list = sdp_list_append(NULL, &l2cap_uuid);
+ if (!l2cap_list)
+ goto fail;
+
+ ccpsm = mcap_get_ctrl_psm(mcap, &err);
+ if (err)
+ goto fail;
+
+ psm = sdp_data_alloc(SDP_UINT16, &ccpsm);
+ if (!psm)
+ goto fail;
+
+ if (!sdp_list_append(l2cap_list, psm))
+ goto fail;
+
+ proto_list = sdp_list_append(NULL, l2cap_list);
+ if (!proto_list)
+ goto fail;
+
+ /* set mcap information */
+ sdp_uuid16_create(&mcap_c_uuid, MCAP_CTRL_UUID);
+ mcap_list = sdp_list_append(NULL, &mcap_c_uuid);
+ if (!mcap_list)
+ goto fail;
+
+ mcap_ver = sdp_data_alloc(SDP_UINT16, &version);
+ if (!mcap_ver)
+ goto fail;
+
+ if (!sdp_list_append(mcap_list, mcap_ver))
+ goto fail;
+
+ if (!sdp_list_append(proto_list, mcap_list))
+ goto fail;
+
+ /* attach protocol information to service record */
+ access_proto_list = sdp_list_append(NULL, proto_list);
+ if (!access_proto_list)
+ goto fail;
+
+ sdp_set_access_protos(rec, access_proto_list);
+ ret = 0;
+
+fail:
+ sdp_list_free(l2cap_list, NULL);
+ sdp_list_free(mcap_list, NULL);
+ sdp_list_free(proto_list, NULL);
+ sdp_list_free(access_proto_list, NULL);
+
+ if (psm)
+ sdp_data_free(psm);
+
+ if (mcap_ver)
+ sdp_data_free(mcap_ver);
+
+ if (err)
+ g_error_free(err);
+
+ return ret;
+}
+
+static int register_service_profiles(sdp_record_t *rec)
+{
+ int ret;
+ sdp_list_t *profile_list;
+ sdp_profile_desc_t hdp_profile;
+
+ DBG("");
+
+ /* set hdp information */
+ sdp_uuid16_create(&hdp_profile.uuid, HDP_SVCLASS_ID);
+ hdp_profile.version = HDP_VERSION;
+ profile_list = sdp_list_append(NULL, &hdp_profile);
+ if (!profile_list)
+ return -1;
+
+ /* set profile descriptor list */
+ ret = sdp_set_profile_descs(rec, profile_list);
+ sdp_list_free(profile_list, NULL);
+
+ return ret;
+}
+
+static int register_service_additional_protocols(sdp_record_t *rec,
+ struct health_app *app)
+{
+ int ret = -1;
+ uuid_t l2cap_uuid, mcap_d_uuid;
+ sdp_list_t *l2cap_list, *proto_list = NULL, *mcap_list = NULL;
+ sdp_list_t *access_proto_list = NULL;
+ sdp_data_t *psm = NULL;
+ uint32_t dcpsm;
+ GError *err = NULL;
+
+ DBG("");
+
+ /* set l2cap information */
+ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+ l2cap_list = sdp_list_append(NULL, &l2cap_uuid);
+ if (!l2cap_list)
+ goto fail;
+
+ dcpsm = mcap_get_ctrl_psm(mcap, &err);
+ if (err)
+ goto fail;
+
+ psm = sdp_data_alloc(SDP_UINT16, &dcpsm);
+ if (!psm)
+ goto fail;
+
+ if (!sdp_list_append(l2cap_list, psm))
+ goto fail;
+
+ proto_list = sdp_list_append(NULL, l2cap_list);
+ if (!proto_list)
+ goto fail;
+
+ /* set mcap information */
+ sdp_uuid16_create(&mcap_d_uuid, MCAP_DATA_UUID);
+ mcap_list = sdp_list_append(NULL, &mcap_d_uuid);
+ if (!mcap_list)
+ goto fail;
+
+ if (!sdp_list_append(proto_list, mcap_list))
+ goto fail;
+
+ /* attach protocol information to service record */
+ access_proto_list = sdp_list_append(NULL, proto_list);
+ if (!access_proto_list)
+ goto fail;
+
+ sdp_set_add_access_protos(rec, access_proto_list);
+ ret = 0;
+
+fail:
+ sdp_list_free(l2cap_list, NULL);
+ sdp_list_free(mcap_list, NULL);
+ sdp_list_free(proto_list, NULL);
+ sdp_list_free(access_proto_list, NULL);
+
+ if (psm)
+ sdp_data_free(psm);
+
+ if (err)
+ g_error_free(err);
+
+ return ret;
+}
+
+static sdp_list_t *mdeps_to_sdp_features(struct mdep_cfg *mdep)
+{
+ sdp_data_t *mdepid, *dtype = NULL, *role = NULL, *descr = NULL;
+ sdp_list_t *f_list = NULL;
+
+ DBG("");
+
+ mdepid = sdp_data_alloc(SDP_UINT8, &mdep->id);
+ if (!mdepid)
+ return NULL;
+
+ dtype = sdp_data_alloc(SDP_UINT16, &mdep->data_type);
+ if (!dtype)
+ goto fail;
+
+ role = sdp_data_alloc(SDP_UINT8, &mdep->role);
+ if (!role)
+ goto fail;
+
+ if (mdep->descr) {
+ descr = sdp_data_alloc(SDP_TEXT_STR8, mdep->descr);
+ if (!descr)
+ goto fail;
+ }
+
+ f_list = sdp_list_append(NULL, mdepid);
+ if (!f_list)
+ goto fail;
+
+ if (!sdp_list_append(f_list, dtype))
+ goto fail;
+
+ if (!sdp_list_append(f_list, role))
+ goto fail;
+
+ if (descr && !sdp_list_append(f_list, descr))
+ goto fail;
+
+ return f_list;
+
+fail:
+ sdp_list_free(f_list, NULL);
+
+ if (mdepid)
+ sdp_data_free(mdepid);
+
+ if (dtype)
+ sdp_data_free(dtype);
+
+ if (role)
+ sdp_data_free(role);
+
+ if (descr)
+ sdp_data_free(descr);
+
+ return NULL;
+}
+
+static void free_hdp_list(void *list)
+{
+ sdp_list_t *hdp_list = list;
+
+ sdp_list_free(hdp_list, (sdp_free_func_t)sdp_data_free);
+}
+
+static void register_features(void *data, void *user_data)
+{
+ struct mdep_cfg *mdep = data;
+ sdp_list_t **sup_features = user_data;
+ sdp_list_t *hdp_feature;
+
+ DBG("");
+
+ hdp_feature = mdeps_to_sdp_features(mdep);
+ if (!hdp_feature)
+ return;
+
+ if (!*sup_features) {
+ *sup_features = sdp_list_append(NULL, hdp_feature);
+ if (!*sup_features)
+ sdp_list_free(hdp_feature,
+ (sdp_free_func_t)sdp_data_free);
+ } else if (!sdp_list_append(*sup_features, hdp_feature)) {
+ sdp_list_free(hdp_feature,
+ (sdp_free_func_t)sdp_data_free);
+ }
+}
+
+static int register_service_sup_features(sdp_record_t *rec,
+ struct health_app *app)
+{
+ sdp_list_t *sup_features = NULL;
+
+ DBG("");
+
+ queue_foreach(app->mdeps, register_features, &sup_features);
+ if (!sup_features)
+ return -1;
+
+ if (sdp_set_supp_feat(rec, sup_features) < 0) {
+ sdp_list_free(sup_features, free_hdp_list);
+ return -1;
+ }
+
+ sdp_list_free(sup_features, free_hdp_list);
+ return 0;
+}
+
+static int register_data_exchange_spec(sdp_record_t *rec)
+{
+ sdp_data_t *spec;
+ uint8_t data_spec = DATA_EXCHANGE_SPEC_11073;
+ /* As of now only 11073 is supported, so we set it as default */
+
+ DBG("");
+
+ spec = sdp_data_alloc(SDP_UINT8, &data_spec);
+ if (!spec)
+ return -1;
+
+ if (sdp_attr_add(rec, SDP_ATTR_DATA_EXCHANGE_SPEC, spec) < 0) {
+ sdp_data_free(spec);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int register_mcap_features(sdp_record_t *rec)
+{
+ sdp_data_t *mcap_proc;
+ uint8_t mcap_sup_proc = MCAP_SUP_PROC;
+
+ DBG("");
+
+ mcap_proc = sdp_data_alloc(SDP_UINT8, &mcap_sup_proc);
+ if (!mcap_proc)
+ return -1;
+
+ if (sdp_attr_add(rec, SDP_ATTR_MCAP_SUPPORTED_PROCEDURES,
+ mcap_proc) < 0) {
+ sdp_data_free(mcap_proc);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int set_sdp_services_uuid(sdp_record_t *rec, uint8_t role)
+{
+ uuid_t source, sink;
+ sdp_list_t *list = NULL;
+
+ sdp_uuid16_create(&sink, HDP_SINK_SVCLASS_ID);
+ sdp_uuid16_create(&source, HDP_SOURCE_SVCLASS_ID);
+ sdp_get_service_classes(rec, &list);
+
+ switch (role) {
+ case HAL_HEALTH_MDEP_ROLE_SOURCE:
+ if (!sdp_list_find(list, &source, sdp_uuid_cmp))
+ list = sdp_list_append(list, &source);
+ break;
+ case HAL_HEALTH_MDEP_ROLE_SINK:
+ if (!sdp_list_find(list, &sink, sdp_uuid_cmp))
+ list = sdp_list_append(list, &sink);
+ break;
+ }
+
+ if (sdp_set_service_classes(rec, list) < 0) {
+ sdp_list_free(list, NULL);
+ return -1;
+ }
+
+ sdp_list_free(list, NULL);
+
+ return 0;
+}
+
+static int update_sdp_record(struct health_app *app)
+{
+ sdp_record_t *rec;
+ uint8_t role;
+
+ DBG("");
+
+ if (record_id > 0) {
+ bt_adapter_remove_record(record_id);
+ record_id = 0;
+ }
+
+ rec = sdp_record_alloc();
+ if (!rec)
+ return -1;
+
+ role = HAL_HEALTH_MDEP_ROLE_SOURCE;
+ if (queue_find(app->mdeps, mdep_by_mdep_role, INT_TO_PTR(role)))
+ set_sdp_services_uuid(rec, role);
+
+ role = HAL_HEALTH_MDEP_ROLE_SINK;
+ if (queue_find(app->mdeps, mdep_by_mdep_role, INT_TO_PTR(role)))
+ set_sdp_services_uuid(rec, role);
+
+ sdp_set_info_attr(rec, app->service_name, app->provider_name,
+ app->service_descr);
+
+ if (register_service_protocols(rec, app) < 0)
+ goto fail;
+
+ if (register_service_profiles(rec) < 0)
+ goto fail;
+
+ if (register_service_additional_protocols(rec, app) < 0)
+ goto fail;
+
+ if (register_service_sup_features(rec, app) < 0)
+ goto fail;
+
+ if (register_data_exchange_spec(rec) < 0)
+ goto fail;
+
+ if (register_mcap_features(rec) < 0)
+ goto fail;
+
+ if (sdp_set_record_state(rec, record_state++) < 0)
+ goto fail;
+
+ if (bt_adapter_add_record(rec, SVC_HINT_HEALTH) < 0) {
+ error("Failed to register HEALTH record");
+ goto fail;
+ }
+
+ record_id = rec->handle;
+
+ return 0;
+
+fail:
+ sdp_record_free(rec);
+
+ return -1;
+}
+
+static struct health_app *create_health_app(const char *app_name,
+ const char *provider, const char *srv_name,
+ const char *srv_descr, uint8_t mdeps)
+{
+ struct health_app *app;
+ static unsigned int app_id = 1;
+
+ DBG("");
+
+ app = new0(struct health_app, 1);
+ if (!app)
+ return NULL;
+
+ app->id = app_id++;
+ app->num_of_mdep = mdeps;
+ app->app_name = strdup(app_name);
+
+ if (provider) {
+ app->provider_name = strdup(provider);
+ if (!app->provider_name)
+ goto fail;
+ }
+
+ if (srv_name) {
+ app->service_name = strdup(srv_name);
+ if (!app->service_name)
+ goto fail;
+ }
+
+ if (srv_descr) {
+ app->service_descr = strdup(srv_descr);
+ if (!app->service_descr)
+ goto fail;
+ }
+
+ return app;
+
+fail:
+ free_health_app(app);
+ return NULL;
+}
static void bt_health_register_app(const void *buf, uint16_t len)
{
- DBG("Not implemented");
+ const struct hal_cmd_health_reg_app *cmd = buf;
+ struct hal_rsp_health_reg_app rsp;
+ struct health_app *app;
+ uint16_t off;
+ uint16_t app_name_len, provider_len, srv_name_len, srv_descr_len;
+ char *app_name, *provider = NULL, *srv_name = NULL, *srv_descr = NULL;
- ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HEALTH, HAL_OP_HEALTH_REG_APP,
- HAL_STATUS_UNSUPPORTED);
+ DBG("");
+
+ if (len != sizeof(*cmd) + cmd->len ||
+ cmd->app_name_off > cmd->provider_name_off ||
+ cmd->provider_name_off > cmd->service_name_off ||
+ cmd->service_name_off > cmd->service_descr_off ||
+ cmd->service_descr_off > cmd->len) {
+ error("health: Invalid register app command, terminating");
+ raise(SIGTERM);
+ return;
+ }
+
+ app_name = (char *) cmd->data;
+ app_name_len = cmd->provider_name_off - cmd->app_name_off;
+
+ off = app_name_len;
+ provider_len = cmd->service_name_off - off;
+ if (provider_len > 0)
+ provider = (char *) cmd->data + off;
+
+ off += provider_len;
+ srv_name_len = cmd->service_descr_off - off;
+ if (srv_name_len > 0)
+ srv_name = (char *) cmd->data + off;
+
+ off += srv_name_len;
+ srv_descr_len = cmd->len - off;
+ if (srv_descr_len > 0)
+ srv_descr = (char *) cmd->data + off;
+
+ app = create_health_app(app_name, provider, srv_name, srv_descr,
+ cmd->num_of_mdep);
+
+ if (!queue_push_tail(apps, app))
+ goto fail;
+
+ rsp.app_id = app->id;
+ ipc_send_rsp_full(hal_ipc, HAL_SERVICE_ID_HEALTH, HAL_OP_HEALTH_REG_APP,
+ sizeof(rsp), &rsp, -1);
+ return;
+
+fail:
+ free_health_app(app);
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HEALTH, HAL_OP_HEALTH_MDEP,
+ HAL_STATUS_FAILED);
+}
+
+static uint8_t android2channel_type(uint8_t type)
+{
+ switch (type) {
+ case HAL_HEALTH_CHANNEL_TYPE_RELIABLE:
+ return CHANNEL_TYPE_RELIABLE;
+ case HAL_HEALTH_CHANNEL_TYPE_STREAMING:
+ return CHANNEL_TYPE_STREAM;
+ default:
+ return CHANNEL_TYPE_ANY;
+ }
}
static void bt_health_mdep_cfg_data(const void *buf, uint16_t len)
{
- DBG("Not implemented");
+ const struct hal_cmd_health_mdep *cmd = buf;
+ struct health_app *app;
+ struct mdep_cfg *mdep = NULL;
+ uint8_t status;
+
+ DBG("");
+
+ app = queue_find(apps, app_by_app_id, INT_TO_PTR(cmd->app_id));
+ if (!app) {
+ status = HAL_STATUS_INVALID;
+ goto fail;
+ }
+
+ mdep = new0(struct mdep_cfg, 1);
+ if (!mdep) {
+ status = HAL_STATUS_INVALID;
+ goto fail;
+ }
+
+ mdep->role = cmd->role;
+ mdep->data_type = cmd->data_type;
+ mdep->channel_type = android2channel_type(cmd->channel_type);
+ mdep->id = queue_length(app->mdeps) + 1;
+
+ if (cmd->descr_len > 0) {
+ mdep->descr = malloc0(cmd->descr_len);
+ memcpy(mdep->descr, cmd->descr, cmd->descr_len);
+ }
+
+ if (app->num_of_mdep > 0 && !app->mdeps) {
+ app->mdeps = queue_new();
+ if (!app->mdeps) {
+ status = HAL_STATUS_FAILED;
+ goto fail;
+ }
+ }
+
+ if (!queue_push_tail(app->mdeps, mdep)) {
+ status = HAL_STATUS_FAILED;
+ goto fail;
+ }
+
+ if (app->num_of_mdep != queue_length(app->mdeps))
+ goto send_rsp;
+
+ /* add sdp record from app configuration data */
+ /*
+ * TODO: Check what to be done if mupltple applications are trying to
+ * register with different role and different configurations.
+ * 1) Does device supports SOURCE and SINK at the same time ?
+ * 2) Does it require different SDP records or one record with
+ * multile MDEP configurations ?
+ */
+ if (update_sdp_record(app) < 0) {
+ error("Error creating HDP SDP record");
+ status = HAL_STATUS_FAILED;
+ goto fail;
+ }
+
+ send_app_reg_notify(app, HAL_HEALTH_APP_REG_SUCCESS);
+
+send_rsp:
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HEALTH, HAL_OP_HEALTH_MDEP,
+ HAL_STATUS_SUCCESS);
+ return;
+
+fail:
+ if (status != HAL_STATUS_SUCCESS) {
+ free_mdep_cfg(mdep);
+ queue_remove(apps, app);
+ free_health_app(app);
+ }
ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HEALTH, HAL_OP_HEALTH_MDEP,
- HAL_STATUS_UNSUPPORTED);
+ status);
}
static void bt_health_unregister_app(const void *buf, uint16_t len)
{
- DBG("Not implemented");
+ const struct hal_cmd_health_unreg_app *cmd = buf;
+ struct health_app *app;
- ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HEALTH, HAL_OP_HEALTH_UNREG_APP,
- HAL_STATUS_UNSUPPORTED);
+ DBG("");
+
+ app = queue_remove_if(apps, app_by_app_id, INT_TO_PTR(cmd->app_id));
+ if (!app) {
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HEALTH,
+ HAL_OP_HEALTH_UNREG_APP, HAL_STATUS_INVALID);
+ return;
+ }
+
+ send_app_reg_notify(app, HAL_HEALTH_APP_DEREG_SUCCESS);
+
+ if (record_id > 0) {
+ bt_adapter_remove_record(record_id);
+ record_id = 0;
+ }
+
+ free_health_app(app);
+ ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HEALTH,
+ HAL_OP_HEALTH_UNREG_APP, HAL_STATUS_SUCCESS);
+}
+
+static int get_prot_desc_entry(sdp_data_t *entry, int type, guint16 *val)
+{
+ sdp_data_t *iter;
+ int proto;
+
+ if (!entry || !SDP_IS_SEQ(entry->dtd))
+ return -1;
+
+ iter = entry->val.dataseq;
+ if (!(iter->dtd & SDP_UUID_UNSPEC))
+ return -1;
+
+ proto = sdp_uuid_to_proto(&iter->val.uuid);
+ if (proto != type)
+ return -1;
+
+ if (!val)
+ return 0;
+
+ iter = iter->next;
+ if (iter->dtd != SDP_UINT16)
+ return -1;
+
+ *val = iter->val.uint16;
+
+ return 0;
+}
+
+static int get_prot_desc_list(const sdp_record_t *rec, uint16_t *psm,
+ uint16_t *version)
+{
+ sdp_data_t *pdl, *p0, *p1;
+
+ if (!psm && !version)
+ return -1;
+
+ pdl = sdp_data_get(rec, SDP_ATTR_PROTO_DESC_LIST);
+ if (!pdl || !SDP_IS_SEQ(pdl->dtd))
+ return -1;
+
+ p0 = pdl->val.dataseq;
+ if (get_prot_desc_entry(p0, L2CAP_UUID, psm) < 0)
+ return -1;
+
+ p1 = p0->next;
+ if (get_prot_desc_entry(p1, MCAP_CTRL_UUID, version) < 0)
+ return -1;
+
+ return 0;
+}
+
+static int get_ccpsm(sdp_list_t *recs, uint16_t *ccpsm)
+{
+ sdp_list_t *l;
+
+ for (l = recs; l; l = l->next) {
+ sdp_record_t *rec = l->data;
+
+ if (!get_prot_desc_list(rec, ccpsm, NULL))
+ return 0;
+ }
+
+ return -1;
+}
+
+static int get_add_prot_desc_list(const sdp_record_t *rec, uint16_t *psm)
+{
+ sdp_data_t *pdl, *p0, *p1;
+
+ if (!psm)
+ return -1;
+
+ pdl = sdp_data_get(rec, SDP_ATTR_ADD_PROTO_DESC_LIST);
+ if (!pdl || pdl->dtd != SDP_SEQ8)
+ return -1;
+
+ pdl = pdl->val.dataseq;
+ if (pdl->dtd != SDP_SEQ8)
+ return -1;
+
+ p0 = pdl->val.dataseq;
+
+ if (get_prot_desc_entry(p0, L2CAP_UUID, psm) < 0)
+ return -1;
+
+ p1 = p0->next;
+ if (get_prot_desc_entry(p1, MCAP_DATA_UUID, NULL) < 0)
+ return -1;
+
+ return 0;
+}
+
+static int get_dcpsm(sdp_list_t *recs, uint16_t *dcpsm)
+{
+ sdp_list_t *l;
+
+ for (l = recs; l; l = l->next) {
+ sdp_record_t *rec = l->data;
+
+ if (!get_add_prot_desc_list(rec, dcpsm))
+ return 0;
+ }
+
+ return -1;
+}
+
+static void mcap_mdl_connected_cb(struct mcap_mdl *mdl, void *data)
+{
+ DBG("Not Implemeneted");
+}
+
+static void mcap_mdl_closed_cb(struct mcap_mdl *mdl, void *data)
+{
+ DBG("Not Implemeneted");
+}
+
+static void mcap_mdl_deleted_cb(struct mcap_mdl *mdl, void *data)
+{
+ DBG("Not Implemeneted");
+}
+
+static void mcap_mdl_aborted_cb(struct mcap_mdl *mdl, void *data)
+{
+ DBG("Not Implemeneted");
+}
+
+static void mcap_mdl_conn_req_cb(struct mcap_mcl *mcl, uint8_t mdepid,
+ uint16_t mdlid, uint8_t *conf, void *data)
+{
+ DBG("Not Implemeneted");
+}
+
+static void mcap_mdl_reconn_req_cb(struct mcap_mdl *mdl, void *data)
+{
+ DBG("Not Implemeneted");
+}
+
+static void create_mcl_cb(struct mcap_mcl *mcl, GError *err, gpointer data)
+{
+ struct health_channel *channel = data;
+ gboolean ret;
+ GError *gerr = NULL;
+
+ DBG("");
+
+ if (err) {
+ error("error creating MCL : %s", err->message);
+ goto fail;
+ }
+
+ if (!channel->dev->mcl)
+ channel->dev->mcl = mcap_mcl_ref(mcl);
+
+ channel->dev->mcl_conn = true;
+ DBG("MCL connected");
+
+ ret = mcap_mcl_set_cb(channel->dev->mcl, channel, &gerr,
+ MCAP_MDL_CB_CONNECTED, mcap_mdl_connected_cb,
+ MCAP_MDL_CB_CLOSED, mcap_mdl_closed_cb,
+ MCAP_MDL_CB_DELETED, mcap_mdl_deleted_cb,
+ MCAP_MDL_CB_ABORTED, mcap_mdl_aborted_cb,
+ MCAP_MDL_CB_REMOTE_CONN_REQ, mcap_mdl_conn_req_cb,
+ MCAP_MDL_CB_REMOTE_RECONN_REQ, mcap_mdl_reconn_req_cb,
+ MCAP_MDL_CB_INVALID);
+ if (!ret) {
+ error("error setting mdl callbacks on mcl");
+
+ if (gerr)
+ g_error_free(gerr);
+
+ goto fail;
+ }
+
+ /* TODO : create mdl */
+ return;
+
+fail:
+ destroy_channel(channel);
+}
+
+static void search_cb(sdp_list_t *recs, int err, gpointer data)
+{
+ struct health_channel *channel = data;
+ GError *gerr = NULL;
+
+ DBG("");
+
+ if (err < 0 || !recs) {
+ error("Error getting remote SDP records");
+ goto fail;
+ }
+
+ if (get_ccpsm(recs, &channel->dev->ccpsm) < 0) {
+ error("Can't get remote PSM for control channel");
+ goto fail;
+ }
+
+ if (get_dcpsm(recs, &channel->dev->dcpsm) < 0) {
+ error("Can't get remote PSM for data channel");
+ goto fail;
+ }
+
+ if (!mcap_create_mcl(mcap, &channel->dev->dst, channel->dev->ccpsm,
+ create_mcl_cb, channel, NULL, &gerr)) {
+ error("error creating mcl %s", gerr->message);
+
+ if (gerr)
+ g_error_free(gerr);
+
+ goto fail;
+ }
+
+ send_channel_state_notify(channel, HAL_HEALTH_CHANNEL_CONNECTING, -1);
+ return;
+
+fail:
+ send_channel_state_notify(channel, HAL_HEALTH_CHANNEL_DESTROYED, -1);
+
+ queue_remove(channel->dev->channels, channel);
+ free_health_channel(channel);
+}
+
+static int connect_mcl(struct health_channel *channel)
+{
+ uuid_t uuid;
+
+ DBG("");
+
+ bt_string2uuid(&uuid, HDP_UUID);
+
+ return bt_search_service(&adapter_addr, &channel->dev->dst, &uuid,
+ search_cb, channel, NULL, 0);
+}
+
+static struct health_device *create_device(uint16_t app_id, const uint8_t *addr)
+{
+ struct health_device *dev;
+
+ dev = new0(struct health_device, 1);
+ if (!dev)
+ return NULL;
+
+ android2bdaddr(addr, &dev->dst);
+ dev->app_id = app_id;
+
+ return dev;
+}
+
+static struct health_channel *create_channel(uint16_t app_id,
+ uint8_t mdep_index)
+{
+ struct health_app *app;
+ struct mdep_cfg *mdep;
+ struct health_channel *channel;
+ uint8_t index;
+ static unsigned int channel_id = 1;
+
+ app = queue_find(apps, app_by_app_id, INT_TO_PTR(app_id));
+ if (!app)
+ return NULL;
+
+ index = mdep_index + 1;
+ mdep = queue_find(app->mdeps, mdep_by_mdep_id, INT_TO_PTR(index));
+ if (!mdep)
+ return NULL;
+
+ channel = new0(struct health_channel, 1);
+ if (!channel)
+ return NULL;
+
+ channel->mdep_id = mdep_index;
+ channel->type = mdep->channel_type;
+ channel->id = channel_id++;
+
+ return channel;
}
static void bt_health_connect_channel(const void *buf, uint16_t len)
{
- DBG("Not implemented");
+ const struct hal_cmd_health_connect_channel *cmd = buf;
+ struct hal_rsp_health_connect_channel rsp;
+ struct health_app *app;
+ struct health_device *dev = NULL;
+ struct health_channel *channel = NULL;
+ DBG("");
+
+ app = queue_find(apps, app_by_app_id, INT_TO_PTR(cmd->app_id));
+ if (!app)
+ goto fail;
+
+ dev = create_device(cmd->app_id, cmd->bdaddr);
+ if (!dev)
+ goto fail;
+
+ channel = create_channel(cmd->app_id, cmd->mdep_index);
+ if (!channel)
+ goto fail;
+
+ channel->dev = dev;
+
+ if (!app->devices) {
+ app->devices = queue_new();
+ if (!app->devices)
+ goto fail;
+ }
+
+ if (!queue_push_tail(app->devices, dev))
+ goto fail;
+
+ if (!dev->channels) {
+ dev->channels = queue_new();
+ if (!dev->channels)
+ goto fail;
+ }
+
+ if (!queue_push_tail(dev->channels, channel)) {
+ queue_remove(app->devices, dev);
+ goto fail;
+ }
+
+ if (connect_mcl(channel) < 0) {
+ error("error retrieving HDP SDP record");
+ queue_remove(app->devices, dev);
+ goto fail;
+ }
+
+ rsp.channel_id = channel->id;
+ ipc_send_rsp_full(hal_ipc, HAL_SERVICE_ID_HEALTH,
+ HAL_OP_HEALTH_CONNECT_CHANNEL,
+ sizeof(rsp), &rsp, -1);
+ return;
+
+fail:
+ free_health_channel(channel);
+ free_health_device(dev);
ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HEALTH,
- HAL_OP_HEALTH_CONNECT_CHANNEL, HAL_STATUS_UNSUPPORTED);
+ HAL_OP_HEALTH_CONNECT_CHANNEL, HAL_STATUS_FAILED);
}
static void bt_health_destroy_channel(const void *buf, uint16_t len)
@@ -104,13 +1224,51 @@
sizeof(struct hal_cmd_health_destroy_channel) },
};
+static void mcl_connected(struct mcap_mcl *mcl, gpointer data)
+{
+ DBG("Not implemented");
+}
+
+static void mcl_reconnected(struct mcap_mcl *mcl, gpointer data)
+{
+ DBG("Not implemented");
+}
+
+static void mcl_disconnected(struct mcap_mcl *mcl, gpointer data)
+{
+ DBG("Not implemented");
+}
+
+static void mcl_uncached(struct mcap_mcl *mcl, gpointer data)
+{
+ DBG("Not implemented");
+}
+
bool bt_health_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode)
{
+ GError *err = NULL;
+
DBG("");
bacpy(&adapter_addr, addr);
+ mcap = mcap_create_instance(&adapter_addr, BT_IO_SEC_MEDIUM, 0, 0,
+ mcl_connected, mcl_reconnected,
+ mcl_disconnected, mcl_uncached,
+ NULL, /* CSP is not used right now */
+ NULL, &err);
+
+ if (!mcap) {
+ error("Error creating MCAP instance : %s", err->message);
+ g_error_free(err);
+ return false;
+ }
+
hal_ipc = ipc;
+ apps = queue_new();
+ if (!apps)
+ return false;
+
ipc_register(hal_ipc, HAL_SERVICE_ID_HEALTH, cmd_handlers,
G_N_ELEMENTS(cmd_handlers));
@@ -121,6 +1279,8 @@
{
DBG("");
+ mcap_instance_unref(mcap);
+ queue_destroy(apps, free_health_app);
ipc_unregister(hal_ipc, HAL_SERVICE_ID_HEALTH);
hal_ipc = NULL;
}
diff --git a/android/hidhost.c b/android/hidhost.c
index 1758020..846dd57 100644
--- a/android/hidhost.c
+++ b/android/hidhost.c
@@ -40,20 +40,22 @@
#include "lib/sdp_lib.h"
#include "src/shared/mgmt.h"
#include "src/shared/util.h"
+#include "src/shared/uhid.h"
#include "src/sdp-client.h"
#include "src/uuid-helper.h"
-#include "profiles/input/uhid_copy.h"
#include "src/log.h"
#include "hal-msg.h"
#include "ipc-common.h"
#include "ipc.h"
+#include "bluetooth.h"
+#include "gatt.h"
+#include "hog.h"
#include "hidhost.h"
#include "utils.h"
#define L2CAP_PSM_HIDP_CTRL 0x11
#define L2CAP_PSM_HIDP_INTR 0x13
-#define UHID_DEVICE_FILE "/dev/uhid"
/* HID message types */
#define HID_MSG_CONTROL 0x10
@@ -78,11 +80,14 @@
/* HID Virtual Cable Unplug */
#define HID_VIRTUAL_CABLE_UNPLUG 0x05
+#define HOG_UUID "00001812-0000-1000-8000-00805f9b34fb"
+
static bdaddr_t adapter_addr;
static GIOChannel *ctrl_io = NULL;
static GIOChannel *intr_io = NULL;
static GSList *devices = NULL;
+static unsigned int hog_app = 0;
static struct ipc *hal_ipc = NULL;
@@ -101,9 +106,10 @@
GIOChannel *intr_io;
guint ctrl_watch;
guint intr_watch;
- int uhid_fd;
- guint uhid_watch_id;
+ struct bt_uhid *uhid;
uint8_t last_hid_msg;
+ struct bt_hog *hog;
+ guint reconnect_id;
};
static int device_cmp(gconstpointer s, gconstpointer user_data)
@@ -114,25 +120,13 @@
return bacmp(&dev->dst, dst);
}
-static void uhid_destroy(int fd)
-{
- struct uhid_event ev;
-
- /* destroy uHID device */
- memset(&ev, 0, sizeof(ev));
- ev.type = UHID_DESTROY;
-
- if (write(fd, &ev, sizeof(ev)) < 0)
- error("hidhost: Failed to destroy uHID device: %s (%d)",
- strerror(errno), errno);
-
- close(fd);
-}
-
static void hid_device_free(void *data)
{
struct hid_device *dev = data;
+ if (dev->reconnect_id > 0)
+ g_source_remove(dev->reconnect_id);
+
if (dev->ctrl_watch > 0)
g_source_remove(dev->ctrl_watch);
@@ -145,13 +139,11 @@
if (dev->ctrl_io)
g_io_channel_unref(dev->ctrl_io);
- if (dev->uhid_watch_id) {
- g_source_remove(dev->uhid_watch_id);
- dev->uhid_watch_id = 0;
- }
+ if (dev->uhid)
+ bt_uhid_unref(dev->uhid);
- if (dev->uhid_fd > 0)
- uhid_destroy(dev->uhid_fd);
+ if (dev->hog)
+ bt_hog_unref(dev->hog);
g_free(dev->rd_data);
g_free(dev);
@@ -196,9 +188,10 @@
return true;
}
-static void handle_uhid_output(struct hid_device *dev,
- struct uhid_output_req *output)
+static void handle_uhid_output(struct uhid_event *event, void *user_data)
{
+ struct uhid_output_req *output = &event->u.output;
+ struct hid_device *dev = user_data;
int fd, req_size;
uint8_t *req;
@@ -222,87 +215,15 @@
free(req);
}
-static gboolean uhid_event_cb(GIOChannel *io, GIOCondition cond,
- gpointer user_data)
-{
- struct hid_device *dev = user_data;
- struct uhid_event ev;
- ssize_t bread;
- int fd;
-
- DBG("");
-
- if (cond & (G_IO_ERR | G_IO_NVAL))
- goto failed;
-
- fd = g_io_channel_unix_get_fd(io);
- memset(&ev, 0, sizeof(ev));
-
- bread = read(fd, &ev, sizeof(ev));
- if (bread < 0) {
- DBG("read: %s (%d)", strerror(errno), errno);
- goto failed;
- }
-
- DBG("uHID event type %d received", ev.type);
-
- switch (ev.type) {
- case UHID_START:
- case UHID_STOP:
- /*
- * These are called to start and stop the underlying hardware.
- * We open the channels before creating the device so the
- * hardware is always ready. No need to handle these.
- * The kernel never destroys a device itself! Only an explicit
- * UHID_DESTROY request can remove a device.
- */
- break;
- case UHID_OPEN:
- case UHID_CLOSE:
- /*
- * OPEN/CLOSE are sent whenever user-space opens any interface
- * provided by the kernel HID device. Whenever the open-count
- * is non-zero we must be ready for I/O. As long as it is zero,
- * we can decide to drop all I/O and put the device
- * asleep This is optional, though.
- */
- break;
- case UHID_OUTPUT:
- handle_uhid_output(dev, &ev.u.output);
- break;
- case UHID_FEATURE:
- /* TODO */
- break;
- case UHID_OUTPUT_EV:
- /*
- * This is only sent by kernels prior to linux-3.11. It
- * requires us to parse HID-descriptors in user-space to
- * properly handle it. This is redundant as the kernel
- * does it already. That's why newer kernels assemble
- * the output-reports and send it to us via UHID_OUTPUT.
- */
- DBG("UHID_OUTPUT_EV unsupported");
- break;
- default:
- warn("unexpected uHID event");
- }
-
- return TRUE;
-
-failed:
- dev->uhid_watch_id = 0;
- return FALSE;
-}
-
static gboolean intr_io_watch_cb(GIOChannel *chan, gpointer data)
{
struct hid_device *dev = data;
uint8_t buf[UHID_DATA_MAX];
struct uhid_event ev;
- int fd, bread;
+ int fd, bread, err;
/* Wait uHID if not ready */
- if (dev->uhid_fd < 0)
+ if (!dev->uhid)
return TRUE;
fd = g_io_channel_unix_get_fd(chan);
@@ -323,8 +244,9 @@
ev.u.input.size = bread - 1;
memcpy(ev.u.input.data, &buf[1], ev.u.input.size);
- if (write(dev->uhid_fd, &ev, sizeof(ev)) < 0)
- DBG("uhid write: %s (%d)", strerror(errno), errno);
+ err = bt_uhid_send(dev->uhid, &ev);
+ if (err < 0)
+ DBG("bt_uhid_send: %s (%d)", strerror(-err), -err);
return TRUE;
}
@@ -571,16 +493,13 @@
static int uhid_create(struct hid_device *dev)
{
- GIOCondition cond = G_IO_IN | G_IO_ERR | G_IO_NVAL;
struct uhid_event ev;
- GIOChannel *io;
int err;
- dev->uhid_fd = open(UHID_DEVICE_FILE, O_RDWR | O_CLOEXEC);
- if (dev->uhid_fd < 0) {
+ dev->uhid = bt_uhid_new_default();
+ if (!dev->uhid) {
err = -errno;
- error("hidhost: Failed to open uHID device: %s",
- strerror(errno));
+ error("hidhost: Failed to create bt_uhid instance");
return err;
}
@@ -595,20 +514,16 @@
ev.u.create.rd_size = dev->rd_size;
ev.u.create.rd_data = dev->rd_data;
- if (write(dev->uhid_fd, &ev, sizeof(ev)) < 0) {
- err = -errno;
+ err = bt_uhid_send(dev->uhid, &ev);
+ if (err < 0) {
error("hidhost: Failed to create uHID device: %s",
- strerror(errno));
- close(dev->uhid_fd);
- dev->uhid_fd = -1;
+ strerror(-err));
+ bt_uhid_unref(dev->uhid);
+ dev->uhid = NULL;
return err;
}
- io = g_io_channel_unix_new(dev->uhid_fd);
- g_io_channel_set_encoding(io, NULL, NULL);
- dev->uhid_watch_id = g_io_add_watch(io, cond, uhid_event_cb, dev);
- g_io_channel_unref(io);
-
+ bt_uhid_register(dev->uhid, UHID_OUTPUT, handle_uhid_output, dev);
bt_hid_set_info(dev);
return 0;
@@ -821,6 +736,89 @@
hid_device_remove(dev);
}
+static gboolean hog_reconnect(void *user_data)
+{
+ struct hid_device *dev = user_data;
+
+ DBG("");
+
+ dev->reconnect_id = 0;
+
+ bt_gatt_connect_app(hog_app, &dev->dst);
+
+ return FALSE;
+}
+
+static void hog_conn_cb(const bdaddr_t *addr, int err, void *attrib)
+{
+ GSList *l;
+ struct hid_device *dev;
+
+ l = g_slist_find_custom(devices, addr, device_cmp);
+ dev = l ? l->data : NULL;
+
+ if (err < 0) {
+ if (!dev)
+ return;
+ if (dev->hog && !dev->reconnect_id) {
+ bt_hid_notify_state(dev,
+ HAL_HIDHOST_STATE_DISCONNECTED);
+ bt_hog_detach(dev->hog);
+ dev->reconnect_id = g_idle_add(hog_reconnect, dev);
+ return;
+ }
+ goto fail;
+ }
+
+ if (!dev) {
+ dev = g_new0(struct hid_device, 1);
+ bacpy(&dev->dst, addr);
+ devices = g_slist_append(devices, dev);
+ bt_hid_notify_state(dev, HAL_HIDHOST_STATE_CONNECTING);
+ }
+
+ if (!dev->hog) {
+ /* TODO: Get device details and primary */
+ dev->hog = bt_hog_new("bluez-input-device", dev->vendor,
+ dev->product, dev->version, NULL);
+ if (!dev->hog) {
+ error("HoG: unable to create session");
+ goto fail;
+ }
+ }
+
+ if (!bt_hog_attach(dev->hog, attrib)) {
+ error("HoG: unable to attach");
+ goto fail;
+ }
+
+ DBG("");
+
+ bt_hid_notify_state(dev, HAL_HIDHOST_STATE_CONNECTED);
+
+ return;
+
+fail:
+ bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTED);
+ hid_device_remove(dev);
+}
+
+static bool hog_connect(struct hid_device *dev)
+{
+ DBG("");
+
+ if (hog_app)
+ return bt_gatt_connect_app(hog_app, &dev->dst);
+
+ hog_app = bt_gatt_register_app(HOG_UUID, GATT_CLIENT, hog_conn_cb);
+ if (!hog_app) {
+ error("hidhost: bt_gatt_register_app failed");
+ return false;
+ }
+
+ return bt_gatt_connect_app(hog_app, &dev->dst);
+}
+
static void bt_hid_connect(const void *buf, uint16_t len)
{
const struct hal_cmd_hidhost_connect *cmd = buf;
@@ -843,11 +841,19 @@
dev = g_new0(struct hid_device, 1);
bacpy(&dev->dst, &dst);
- dev->uhid_fd = -1;
+ dev->state = HAL_HIDHOST_STATE_DISCONNECTED;
ba2str(&dev->dst, addr);
DBG("connecting to %s", addr);
+ if (bt_is_device_le(&dst)) {
+ if (!hog_connect(dev)) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+ goto done;
+ }
+
sdp_uuid16_create(&uuid, PNP_INFO_SVCLASS_ID);
if (bt_search_service(&adapter_addr, &dev->dst, &uuid,
hid_sdp_did_search_cb, dev, NULL, 0) < 0) {
@@ -857,8 +863,11 @@
goto failed;
}
+done:
devices = g_slist_append(devices, dev);
- bt_hid_notify_state(dev, HAL_HIDHOST_STATE_CONNECTING);
+
+ if (dev->state == HAL_HIDHOST_STATE_DISCONNECTED)
+ bt_hid_notify_state(dev, HAL_HIDHOST_STATE_CONNECTING);
status = HAL_STATUS_SUCCESS;
@@ -867,6 +876,23 @@
status);
}
+static bool hog_disconnect(struct hid_device *dev)
+{
+ DBG("");
+
+ if (dev->state == HAL_HIDHOST_STATE_DISCONNECTED)
+ return false;
+
+ bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTING);
+
+ if (!bt_gatt_disconnect_app(hog_app, &dev->dst)) {
+ bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTED);
+ hid_device_remove(dev);
+ }
+
+ return true;
+}
+
static void bt_hid_disconnect(const void *buf, uint16_t len)
{
const struct hal_cmd_hidhost_disconnect *cmd = buf;
@@ -886,6 +912,13 @@
}
dev = l->data;
+ if (bt_is_device_le(&dst)) {
+ if (!hog_disconnect(dev)) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+ goto done;
+ }
/* Wait either channels to HUP */
if (dev->intr_io)
@@ -896,6 +929,8 @@
bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTING);
+
+done:
status = HAL_STATUS_SUCCESS;
failed:
@@ -1361,7 +1396,6 @@
dev = g_new0(struct hid_device, 1);
bacpy(&dev->dst, &dst);
dev->ctrl_io = g_io_channel_ref(chan);
- dev->uhid_fd = -1;
sdp_uuid16_create(&uuid, PNP_INFO_SVCLASS_ID);
if (bt_search_service(&adapter_addr, &dev->dst, &uuid,
@@ -1443,6 +1477,9 @@
{
DBG("");
+ if (hog_app > 0)
+ bt_gatt_unregister_app(hog_app);
+
g_slist_free_full(devices, hid_device_free);
devices = NULL;
diff --git a/android/hog.c b/android/hog.c
new file mode 100644
index 0000000..d42400d
--- /dev/null
+++ b/android/hog.c
@@ -0,0 +1,862 @@
+/*
+ *
+ * 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 <bluetooth/bluetooth.h>
+
+#include <glib.h>
+
+#include "src/log.h"
+
+#include "lib/uuid.h"
+#include "src/shared/util.h"
+#include "src/shared/uhid.h"
+
+#include "attrib/att.h"
+#include "attrib/gattrib.h"
+#include "attrib/gatt.h"
+
+#include "btio/btio.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;
+ gboolean has_report_id;
+ uint16_t bcdhid;
+ uint8_t bcountrycode;
+ uint16_t proto_mode_handle;
+ uint16_t ctrlpt_handle;
+ uint8_t flags;
+};
+
+struct report {
+ struct bt_hog *hog;
+ uint8_t id;
+ uint8_t type;
+ guint notifyid;
+ struct gatt_char *decl;
+};
+
+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 report *report = user_data;
+ struct bt_hog *hog = report->hog;
+
+ 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(uint16_t handle, gpointer user_data)
+{
+ struct report *report = user_data;
+ struct bt_hog *hog = report->hog;
+ uint8_t value[] = { 0x01, 0x00 };
+
+ gatt_write_char(hog->attrib, handle, value, sizeof(value),
+ report_ccc_written_cb, report);
+}
+
+static void report_reference_cb(guint8 status, const guint8 *pdu,
+ guint16 plen, gpointer user_data)
+{
+ struct report *report = user_data;
+
+ 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]);
+}
+
+static void external_report_reference_cb(guint8 status, const guint8 *pdu,
+ guint16 plen, gpointer user_data);
+
+
+static void discover_descriptor_cb(uint8_t status, GSList *descs,
+ void *user_data)
+{
+ struct report *report;
+ struct bt_hog *hog;
+ GAttrib *attrib = NULL;
+
+ if (status != 0) {
+ error("Discover all 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 = user_data;
+ attrib = report->hog->attrib;
+ write_ccc(desc->handle, report);
+ break;
+ case GATT_REPORT_REFERENCE:
+ report = user_data;
+ attrib = report->hog->attrib;
+ gatt_read_char(attrib, desc->handle,
+ report_reference_cb, report);
+ break;
+ case GATT_EXTERNAL_REPORT_REFERENCE:
+ hog = user_data;
+ attrib = hog->attrib;
+ gatt_read_char(attrib, desc->handle,
+ external_report_reference_cb, hog);
+ break;
+ }
+ }
+}
+
+static void discover_descriptor(GAttrib *attrib, uint16_t start, uint16_t end,
+ gpointer user_data)
+{
+ if (start > end)
+ return;
+
+ gatt_discover_desc(attrib, start, end, NULL,
+ discover_descriptor_cb, user_data);
+}
+
+static void external_service_char_cb(uint8_t status, GSList *chars,
+ void *user_data)
+{
+ struct bt_hog *hog = user_data;
+ struct gatt_primary *primary = hog->primary;
+ struct report *report;
+ GSList *l;
+
+ 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 = g_new0(struct report, 1);
+ report->hog = hog;
+ report->decl = g_memdup(chr, sizeof(*chr));
+ hog->reports = g_slist_append(hog->reports, report);
+ start = chr->value_handle + 1;
+ end = (next ? next->handle - 1 : primary->range.end);
+ discover_descriptor(hog->attrib, start, end, report);
+ }
+}
+
+static void external_report_reference_cb(guint8 status, const guint8 *pdu,
+ guint16 plen, gpointer user_data)
+{
+ struct bt_hog *hog = user_data;
+ uint16_t uuid16;
+ bt_uuid_t uuid;
+
+ 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);
+ bt_uuid16_create(&uuid, uuid16);
+ gatt_discover_char(hog->attrib, 0x00, 0xff, &uuid,
+ external_service_char_cb, hog);
+}
+
+static int report_type_cmp(gconstpointer a, gconstpointer b)
+{
+ const struct report *report = a;
+ uint8_t type = GPOINTER_TO_UINT(b);
+
+ return report->type - type;
+}
+
+static void output_written_cb(guint8 status, const guint8 *pdu,
+ guint16 plen, gpointer user_data)
+{
+ 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;
+ GSList *l;
+ void *data;
+ int size;
+ guint type;
+
+ if (hog->has_report_id) {
+ data = ev->u.output.data + 1;
+ size = ev->u.output.size - 1;
+ } else {
+ data = ev->u.output.data;
+ size = ev->u.output.size;
+ }
+
+ switch (ev->type) {
+ case UHID_OUTPUT:
+ type = HOG_REPORT_TYPE_OUTPUT;
+ break;
+ case UHID_FEATURE:
+ type = HOG_REPORT_TYPE_FEATURE;
+ break;
+ default:
+ return;
+ }
+
+ l = g_slist_find_custom(hog->reports, GUINT_TO_POINTER(type),
+ report_type_cmp);
+ if (!l)
+ return;
+
+ report = l->data;
+
+ DBG("Sending report type %d to handle 0x%X", type,
+ report->decl->value_handle);
+
+ if (hog->attrib == NULL)
+ return;
+
+ if (report->decl->properties & GATT_CHR_PROP_WRITE)
+ gatt_write_char(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 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 bt_hog *hog = 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;
+
+ 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, forward_report, hog);
+}
+
+static void info_read_cb(guint8 status, const guint8 *pdu, guint16 plen,
+ gpointer user_data)
+{
+ struct bt_hog *hog = user_data;
+ uint8_t value[HID_INFO_SIZE];
+ ssize_t vlen;
+
+ 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 bt_hog *hog = user_data;
+ uint8_t value;
+ ssize_t vlen;
+
+ 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 bt_hog *hog = 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;
+
+ 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 = g_new0(struct report, 1);
+ report->hog = hog;
+ report->decl = g_memdup(chr, sizeof(*chr));
+ hog->reports = g_slist_append(hog->reports,
+ report);
+ discover_descriptor(hog->attrib, start, end, report);
+ } else if (bt_uuid_cmp(&uuid, &report_map_uuid) == 0) {
+ gatt_read_char(hog->attrib, chr->value_handle,
+ report_map_read_cb, hog);
+ discover_descriptor(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;
+ gatt_read_char(hog->attrib, proto_mode_handle,
+ proto_mode_read_cb, hog);
+ }
+
+ if (info_handle)
+ gatt_read_char(hog->attrib, info_handle, info_read_cb,
+ hog);
+}
+
+static void report_free(void *data)
+{
+ struct report *report = data;
+ struct bt_hog *hog = report->hog;
+
+ if (hog->attrib)
+ g_attrib_unregister(hog->attrib, report->notifyid);
+
+ g_free(report->decl);
+ g_free(report);
+}
+
+static void hog_free(struct bt_hog *hog)
+{
+ bt_uhid_unref(hog->uhid);
+ g_slist_free_full(hog->reports, report_free);
+ g_attrib_unref(hog->attrib);
+ g_free(hog->name);
+ g_free(hog->primary);
+ g_free(hog);
+}
+
+struct bt_hog *bt_hog_new(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->uhid = bt_uhid_new_default();
+ if (!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 bt_hog *hog = user_data;
+ struct gatt_included *include;
+ GSList *l;
+
+ DBG("");
+
+ if (hog->primary)
+ return;
+
+ if (status) {
+ const char *str = att_ecode2str(status);
+ DBG("Find included failed: %s", str);
+ return;
+ }
+
+ if (!services) {
+ DBG("No included service found");
+ return;
+ }
+
+ for (l = services; l; l = l->next) {
+ include = l->data;
+
+ if (strcmp(include->uuid, HOG_UUID) == 0)
+ break;
+ }
+
+ if (!l) {
+ for (l = services; l; l = l->next) {
+ include = l->data;
+
+ gatt_find_included(hog->attrib, include->range.start,
+ include->range.end, find_included_cb, hog);
+ }
+ return;
+ }
+
+ hog->primary = g_new0(struct gatt_primary, 1);
+ memcpy(hog->primary->uuid, include->uuid, sizeof(include->uuid));
+ memcpy(&hog->primary->range, &include->range, sizeof(include->range));
+
+ gatt_discover_char(hog->attrib, hog->primary->range.start,
+ hog->primary->range.end, NULL,
+ char_discovered_cb, hog);
+}
+
+static void primary_cb(uint8_t status, GSList *services, void *user_data)
+{
+ struct bt_hog *hog = user_data;
+ struct gatt_primary *primary;
+ GSList *l;
+
+ DBG("");
+
+ 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, HOG_UUID) == 0)
+ break;
+ }
+
+ if (!l) {
+ for (l = services; l; l = l->next) {
+ primary = l->data;
+
+ gatt_find_included(hog->attrib, primary->range.start,
+ primary->range.end, find_included_cb,
+ hog);
+ }
+ return;
+ }
+
+ hog->primary = g_memdup(primary, sizeof(*primary));
+
+ gatt_discover_char(hog->attrib, hog->primary->range.start,
+ hog->primary->range.end, NULL,
+ char_discovered_cb, hog);
+}
+
+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) {
+ gatt_discover_primary(hog->attrib, NULL, primary_cb, hog);
+ return true;
+ }
+
+ if (hog->reports == NULL) {
+ gatt_discover_char(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;
+
+ for (l = hog->reports; l; l = l->next) {
+ struct report *r = l->data;
+
+ g_attrib_unregister(hog->attrib, r->notifyid);
+ }
+
+ 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;
+}
diff --git a/android/hog.h b/android/hog.h
new file mode 100644
index 0000000..7cf446b
--- /dev/null
+++ b/android/hog.h
@@ -0,0 +1,35 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2014 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 bt_hog;
+
+struct bt_hog *bt_hog_new(const char *name, uint16_t vendor, uint16_t product,
+ uint16_t version, void *primary);
+
+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);
diff --git a/android/ipc-tester.c b/android/ipc-tester.c
index 63fd105..f1f93f2 100644
--- a/android/ipc-tester.c
+++ b/android/ipc-tester.c
@@ -279,7 +279,8 @@
failed:
close(pipe);
- close(fd);
+ if (fd >= 0)
+ close(fd);
}
static int accept_connection(int sk)
diff --git a/android/ipc.c b/android/ipc.c
index 8cd34ea..2e67428 100644
--- a/android/ipc.c
+++ b/android/ipc.c
@@ -40,6 +40,11 @@
#include "ipc.h"
#include "src/log.h"
+struct service_handler {
+ const struct ipc_handler *handler;
+ uint8_t size;
+};
+
struct ipc {
struct service_handler *services;
int service_max;
@@ -399,11 +404,17 @@
void ipc_send_notif(struct ipc *ipc, uint8_t service_id, uint8_t opcode,
uint16_t len, void *param)
{
+ return ipc_send_notif_with_fd(ipc, service_id, opcode, len, param, -1);
+}
+
+void ipc_send_notif_with_fd(struct ipc *ipc, uint8_t service_id, uint8_t opcode,
+ uint16_t len, void *param, int fd)
+{
if (!ipc || !ipc->notif_io)
return;
ipc_send(g_io_channel_unix_get_fd(ipc->notif_io), service_id, opcode,
- len, param, -1);
+ len, param, fd);
}
void ipc_register(struct ipc *ipc, uint8_t service,
diff --git a/android/ipc.h b/android/ipc.h
index cc4e92d..fd2b985 100644
--- a/android/ipc.h
+++ b/android/ipc.h
@@ -27,11 +27,6 @@
size_t data_len;
};
-struct service_handler {
- const struct ipc_handler *handler;
- uint8_t size;
-};
-
struct ipc;
typedef void (*ipc_disconnect_cb) (void *data);
@@ -47,6 +42,9 @@
uint16_t len, void *param, int fd);
void ipc_send_notif(struct ipc *ipc, uint8_t service_id, uint8_t opcode,
uint16_t len, void *param);
+void ipc_send_notif_with_fd(struct ipc *ipc, uint8_t service_id, uint8_t opcode,
+ uint16_t len, void *param, int fd);
+
void ipc_register(struct ipc *ipc, uint8_t service,
const struct ipc_handler *handlers, uint8_t size);
void ipc_unregister(struct ipc *ipc, uint8_t service);
diff --git a/android/pics-a2dp.txt b/android/pics-a2dp.txt
index 81debcd..53b3c1c 100644
--- a/android/pics-a2dp.txt
+++ b/android/pics-a2dp.txt
@@ -50,7 +50,7 @@
Parameter Name Selected Description
-------------------------------------------------------------------------------
TSPC_A2DP_3_1 True SRC: SBC encoder Codec (M)
-TSPC_A2DP_3_2 False SRC: Additional encoder Codec (O)
+TSPC_A2DP_3_2 True (*) SRC: Additional encoder Codec (O)
-------------------------------------------------------------------------------
diff --git a/android/pics-gap.txt b/android/pics-gap.txt
index cd48332..b3b896c 100644
--- a/android/pics-gap.txt
+++ b/android/pics-gap.txt
@@ -61,7 +61,7 @@
TSPC_GAP_1_3 True (*) General-discoverable mode (O)
TSPC_GAP_1_4 True (*) Non-connectable mode (O)
TSPC_GAP_1_5 True Connectable mode (M)
-TSPC_GAP_1_6 False Non-bondable mode (O)
+TSPC_GAP_1_6 True (*) Non-bondable mode (O)
TSPC_GAP_1_7 True (*) Bondable mode (C.2)
TSPC_GAP_1_8 False Non-Synchronizable Mode (O)
TSPC_GAP_1_9 False Synchronizable Mode (O)
@@ -149,7 +149,7 @@
-------------------------------------------------------------------------------
TSPC_GAP_5_1 False (*) Broadcaster (C.1)
TSPC_GAP_5_2 False Observer (C.1)
-TSPC_GAP_5_3 False (*) Peripheral (C.1)
+TSPC_GAP_5_3 True Peripheral (C.1)
TSPC_GAP_5_4 True (*) Central (C.1)
-------------------------------------------------------------------------------
C.1: It is mandatory to support at least one of the defined roles.
@@ -306,8 +306,8 @@
-------------------------------------------------------------------------------
Parameter Name Selected Description
-------------------------------------------------------------------------------
-TSPC_GAP_18_1 False Peripheral: Transmitter
-TSPC_GAP_18_2 False Peripheral: Receiver
+TSPC_GAP_18_1 True (*) Peripheral: Transmitter
+TSPC_GAP_18_2 True (*) Peripheral: Receiver
-------------------------------------------------------------------------------
@@ -315,9 +315,9 @@
-------------------------------------------------------------------------------
Parameter Name Selected Description
-------------------------------------------------------------------------------
-TSPC_GAP_19_1 False Peripheral: Standby
-TSPC_GAP_19_2 False Peripheral: Advertising
-TSPC_GAP_19_3 False Peripheral: Connection, Slave Role
+TSPC_GAP_19_1 True (*) Peripheral: Standby
+TSPC_GAP_19_2 True (*) Peripheral: Advertising
+TSPC_GAP_19_3 True (*) Peripheral: Connection, Slave Role
-------------------------------------------------------------------------------
@@ -325,11 +325,11 @@
-------------------------------------------------------------------------------
Parameter Name Selected Description
-------------------------------------------------------------------------------
-TSPC_GAP_20_1 False Peripheral: Connectable Undirected Event (C.1)
-TSPC_GAP_20_2 False Peripheral: Connectable Directed Event (C.2)
-TSPC_GAP_20_2A False Peripheral: Low Duty Directed Advertising (C.3)
-TSPC_GAP_20_3 False Peripheral: Non-Connectable Undirected Event
-TSPC_GAP_20_4 False Peripheral: Scannable Undirected Event
+TSPC_GAP_20_1 True (*) Peripheral: Connectable Undirected Event (C.1)
+TSPC_GAP_20_2 True (*) Peripheral: Connectable Directed Event (C.2)
+TSPC_GAP_20_2A True (*) Peripheral: Low Duty Directed Advertising (C.3)
+TSPC_GAP_20_3 True (*) Peripheral: Non-Connectable Undirected Event
+TSPC_GAP_20_4 True (*) Peripheral: Scannable Undirected Event
-------------------------------------------------------------------------------
@@ -338,10 +338,10 @@
Parameter Name Selected Description
-------------------------------------------------------------------------------
TSPC_GAP_20A_1 False AD Type-Service UUID (C.1)
-TSPC_GAP_20A_2 False AD Type-Local Name (C.1)
-TSPC_GAP_20A_3 False AD Type-Flags (C.2)
+TSPC_GAP_20A_2 True (*) AD Type-Local Name (C.1)
+TSPC_GAP_20A_3 True (*) AD Type-Flags (C.2)
TSPC_GAP_20A_4 False AD Type-Manufacturer Specific Data (C.1)
-TSPC_GAP_20A_5 False AD Type-TX Power Level (C.1)
+TSPC_GAP_20A_5 True (*) AD Type-TX Power Level (C.1)
TSPC_GAP_20A_6 False AD Type-Security Manager Out of Band (OOB) (C.3)
TSPC_GAP_20A_7 False AD Type-Security manager TK Value (C.1)
TSPC_GAP_20A_8 False AD Type-Slave Connection Interval Range (C.1)
@@ -369,16 +369,16 @@
-------------------------------------------------------------------------------
Parameter Name Selected Description
-------------------------------------------------------------------------------
-TSPC_GAP_21_1 False (*) Peripheral: Connection Update Procedure (M)
-TSPC_GAP_21_2 False (*) Peripheral: Channel Map Update Procedure (M)
-TSPC_GAP_21_3 False Peripheral: Encryption Procedure (O)
-TSPC_GAP_21_4 False (*) Peripheral: Feature Exchange Procedure (M)
-TSPC_GAP_21_5 False (*) Peripheral: Version Exchange Procedure (M)
-TSPC_GAP_21_6 False (*) Peripheral: Termination Procedure (M)
-TSPC_GAP_21_7 False (*) Peripheral: LE Ping Procedure (C.3)
-TSPC_GAP_21_8 False (*) Peripheral: Slave Initiated Feature Exchange
+TSPC_GAP_21_1 True Peripheral: Connection Update Procedure (M)
+TSPC_GAP_21_2 True Peripheral: Channel Map Update Procedure (M)
+TSPC_GAP_21_3 True (*) Peripheral: Encryption Procedure (O)
+TSPC_GAP_21_4 True Peripheral: Feature Exchange Procedure (M)
+TSPC_GAP_21_5 True Peripheral: Version Exchange Procedure (M)
+TSPC_GAP_21_6 True Peripheral: Termination Procedure (M)
+TSPC_GAP_21_7 True Peripheral: LE Ping Procedure (C.3)
+TSPC_GAP_21_8 True Peripheral: Slave Initiated Feature Exchange
Procedure (C.4)
-TSPC_GAP_21_9 False (*) Peripheral: Connection Parameter Request
+TSPC_GAP_21_9 True Peripheral: Connection Parameter Request
Procedure (C.5)
-------------------------------------------------------------------------------
@@ -387,10 +387,10 @@
-------------------------------------------------------------------------------
Parameter Name Selected Description
-------------------------------------------------------------------------------
-TSPC_GAP_22_1 False Peripheral: Non-Discoverable Mode (C.2)
-TSPC_GAP_22_2 False Peripheral: Limited Discoverable Mode (C.1)
-TSPC_GAP_22_3 False Peripheral: General Discoverable Mode (C.1)
-TSPC_GAP_22_4 False Peripheral: Name Discovery Procedure (C.3)
+TSPC_GAP_22_1 True (*) Peripheral: Non-Discoverable Mode (C.2)
+TSPC_GAP_22_2 True (*) Peripheral: Limited Discoverable Mode (C.1)
+TSPC_GAP_22_3 True (*) Peripheral: General Discoverable Mode (C.1)
+TSPC_GAP_22_4 True (*) Peripheral: Name Discovery Procedure (C.3)
-------------------------------------------------------------------------------
C.1: Optional if (TSPC_GAP_5_3 OR TSPC_GAP_42_2), otherwise Excluded.
C.2: Mandatory if (TSPC_GAP_5_3 or TSPC_GAP_42_1) is supported,
@@ -403,12 +403,12 @@
-------------------------------------------------------------------------------
Parameter Name Selected Description
-------------------------------------------------------------------------------
-TSPC_GAP_23_1 False Peripheral: Non-Connectable Mode (C.1)
-TSPC_GAP_23_2 False Peripheral: Directed Connectable Mode (O)
-TSPC_GAP_23_3 False Peripheral: Undirected Connectable Mode (M)
-TSPC_GAP_23_4 False Peripheral: Connection Parameter Update
+TSPC_GAP_23_1 False (*) Peripheral: Non-Connectable Mode (C.1)
+TSPC_GAP_23_2 True (*) Peripheral: Directed Connectable Mode (O)
+TSPC_GAP_23_3 True (*) Peripheral: Undirected Connectable Mode (M)
+TSPC_GAP_23_4 True (*) Peripheral: Connection Parameter Update
Procedure (O)
-TSPC_GAP_23_5 False Peripheral: Terminate Connection Procedure (M)
+TSPC_GAP_23_5 True (*) Peripheral: Terminate Connection Procedure (M)
-------------------------------------------------------------------------------
C.1: Mandatory if TSPC_GAP_5_3 (LE Only – Peripheral role) OR TSPC_GAP_42_3
(BR/EDR/LE – Non-Connectable Mode) OR TSPC_GAP_42_4
@@ -420,10 +420,10 @@
-------------------------------------------------------------------------------
Parameter Name Selected Description
-------------------------------------------------------------------------------
-TSPC_GAP_24_1 False Peripheral: Non-Bondable Mode (M)
-TSPC_GAP_24_2 False Peripheral: Bondable Mode (C.1)
-TSPC_GAP_24_3 False Peripheral: Bonding Procedure (C.2)
-TSPC_GAP_24_4 False Peripheral: Multiple Bonds (C.3)
+TSPC_GAP_24_1 True (*) Peripheral: Non-Bondable Mode (M)
+TSPC_GAP_24_2 True (*) Peripheral: Bondable Mode (C.1)
+TSPC_GAP_24_3 True (*) Peripheral: Bonding Procedure (C.2)
+TSPC_GAP_24_4 True (*) Peripheral: Multiple Bonds (C.3)
-------------------------------------------------------------------------------
C.1: Optional if TSPC_GAP_5_3 (LE Only – Peripheral role) OR (TSPC_GAP_38_3
(BR/EDR/LE – Peripheral role) AND NOT TSPC_GAP_42_6 (BR.EDR/LE -
@@ -437,18 +437,18 @@
-------------------------------------------------------------------------------
Parameter Name Selected Description
-------------------------------------------------------------------------------
-TSPC_GAP_25_1 False Peripheral: Security Mode (O)
-TSPC_GAP_25_2 False Peripheral: Security Mode 2 (O)
-TSPC_GAP_25_3 False Peripheral: Authentication Procedure (C.2)
-TSPC_GAP_25_4 False Peripheral: Authorization Procedure (O)
-TSPC_GAP_25_5 False Peripheral: Connection Data Signing Procedure
- (O)
-TSPC_GAP_25_6 False Peripheral: Authenticate Signed Data Procedure
- (O)
-TSPC_GAP_25_7 False Peripheral: Authenticated Pairing
- (LE security mode 1 level 3) (C.1)
-TSPC_GAP_25_8 False Peripheral: Unauthenticated Pairing
- (LE security mode 1 level 2) (C.1)
+TSPC_GAP_25_1 True (*) Peripheral: Security Mode (O)
+TSPC_GAP_25_2 True (*) Peripheral: Security Mode 2 (O)
+TSPC_GAP_25_3 True (*) Peripheral: Authentication Procedure (C.2)
+TSPC_GAP_25_4 True (*) Peripheral: Authorization Procedure (O)
+TSPC_GAP_25_5 True (*) Peripheral: Connection Data Signing Procedure
+ (O)
+TSPC_GAP_25_6 True (*) Peripheral: Authenticate Signed Data Procedure
+ (O)
+TSPC_GAP_25_7 True (*) Peripheral: Authenticated Pairing
+ (LE security mode 1 level 3) (C.1)
+TSPC_GAP_25_8 True (*) Peripheral: Unauthenticated Pairing
+ (LE security mode 1 level 2) (C.1)
-------------------------------------------------------------------------------
C.1: Optional if TSPC_GAP_25_1 is supported, otherwise Excluded.
C.2: Mandatory if TSPC_GAP_0A_1 and TSPC_GAP_27_4 are supported,
@@ -461,12 +461,12 @@
Parameter Name Selected Description
-------------------------------------------------------------------------------
TSPC_GAP_26_1 False Peripheral: Privacy Feature (O)
-TSPC_GAP_26_1A False Peripheral: Privacy Feature v1.1 (O)
-TSPC_GAP_26_2 False Peripheral: Non-Resolvable Private Address
+TSPC_GAP_26_1A True (*) Peripheral: Privacy Feature v1.1 (O)
+TSPC_GAP_26_2 True (*) Peripheral: Non-Resolvable Private Address
Generation Procedure (C.1)
-TSPC_GAP_26_3 False Peripheral: Resolvable Private Address
+TSPC_GAP_26_3 True (*) Peripheral: Resolvable Private Address
Generation Procedure (C.2)
-TSPC_GAP_26_4 False Peripheral: Resolvable Private Address
+TSPC_GAP_26_4 True (*) Peripheral: Resolvable Private Address
Generation Procedure (C.4)
-------------------------------------------------------------------------------
C.1: Optional if TSPC_GAP_26_1 is supported, otherwise Excluded.
@@ -478,8 +478,8 @@
-------------------------------------------------------------------------------
Parameter Name Selected Description
-------------------------------------------------------------------------------
-TSPC_GAP_27_1 False (*) Peripheral: Device Name (M)
-TSPC_GAP_27_2 False (*) Peripheral: Appearance (M)
+TSPC_GAP_27_1 True Peripheral: Device Name (M)
+TSPC_GAP_27_2 True Peripheral: Appearance (M)
TSPC_GAP_27_3 False Peripheral: Peripheral Privacy Flag (C.1)
TSPC_GAP_27_4 False Peripheral: Reconnection Address (C.2)
TSPC_GAP_27_5 False Peripheral: Peripheral Preferred Connection
@@ -532,12 +532,12 @@
-------------------------------------------------------------------------------
Parameter Name Selected Description
-------------------------------------------------------------------------------
-TSPC_GAP_31_1 True () Central: Connection Update Procedure (M)
-TSPC_GAP_31_2 True () Central: Channel Map Update Procedure (M)
+TSPC_GAP_31_1 True (*) Central: Connection Update Procedure (M)
+TSPC_GAP_31_2 True (*) Central: Channel Map Update Procedure (M)
TSPC_GAP_31_3 True (*) Central: Encryption Procedure (O)
-TSPC_GAP_31_4 True () Central: Feature Exchange Procedure (M)
-TSPC_GAP_31_5 True () Central: Version Exchange Procedure (M)
-TSPC_GAP_31_6 True () Central: Termination Procedure (M)
+TSPC_GAP_31_4 True (*) Central: Feature Exchange Procedure (M)
+TSPC_GAP_31_5 True (*) Central: Version Exchange Procedure (M)
+TSPC_GAP_31_6 True (*) Central: Termination Procedure (M)
TSPC_GAP_31_7 True (*) Central: LE Ping Procedure (C.1)
TSPC_GAP_31_8 True (*) Central: Slave Initiated Feature Exchange
Procedure (C.2)
@@ -572,7 +572,7 @@
Procedure (C.1)
TSPC_GAP_33_3 True (*) Central: Selective Connection Establishment
Procedure (C.3)
-TSPC_GAP_33_4 True (*) Central: Direct Connectin Establishment
+TSPC_GAP_33_4 True (*) Central: Direct Connection Establishment
Procedure (C.2)
TSPC_GAP_33_5 True (*) Central: Connection Parameter Update Procedure
(C.2)
@@ -656,7 +656,7 @@
-------------------------------------------------------------------------------
TSPC_GAP_38_1 False BR/EDR/LE: Broadcaster (C.1)
TSPC_GAP_38_2 False BR/EDR/LE: Observer (C.1)
-TSPC_GAP_38_3 False BR/EDR/LE: Peripheral (C.1)
+TSPC_GAP_38_3 True (*) BR/EDR/LE: Peripheral (C.1)
TSPC_GAP_38_4 True (*) BR/EDR/LE: Central (C.1)
-------------------------------------------------------------------------------
C.1: It is mandatory to support at least one of the defined roles.
@@ -672,8 +672,8 @@
TSPC_GAP_39_1 True (*) Central BR/EDR/LE: Non-Discoverable Mode (C.1)
TSPC_GAP_39_2 True (*) Central BR/EDR/LE: Discoverable Mode (C.2)
TSPC_GAP_39_3 True (*) Central BR/EDR/LE: Non-Connectable Mode (C.3)
-TSPC_GAP_39_4 True () Central BR/EDR/LE: Connectable Mode (M)
-TSPC_GAP_39_5 False Central BR/EDR/LE: Non-Bondable Mode (C.4)
+TSPC_GAP_39_4 True (*) Central BR/EDR/LE: Connectable Mode (M)
+TSPC_GAP_39_5 True (*) Central BR/EDR/LE: Non-Bondable Mode (C.4)
TSPC_GAP_39_6 True (*) Central BR/EDR/LE: Bondable Mode (C.5)
-------------------------------------------------------------------------------
C.1: Mandatory if TSPC_GAP_1_1 is supported over BR/EDR, otherwise Excluded.
@@ -717,12 +717,16 @@
-------------------------------------------------------------------------------
Parameter Name Selected Description
-------------------------------------------------------------------------------
-TSPC_GAP_42_1 False Peripheral BR/EDR/LE: Non-Discoverable Mode (See Spec)
-TSPC_GAP_42_2 False Peripheral BR/EDR/LE: Discoverable Mode (See Spec)
-TSPC_GAP_42_3 False Peripheral BR/EDR/LE: Non-Connectable Mode (See Spec)
-TSPC_GAP_42_4 False (*) Peripheral BR/EDR/LE: Connectable Mode (M)
-TSPC_GAP_42_5 False Peripheral BR/EDR/LE: Non-Bondable Mode (See Spec)
-TSPC_GAP_42_6 False Peripheral BR/EDR/LE: Bondable Mode (See Spec)
+TSPC_GAP_42_1 True (*) Peripheral BR/EDR/LE: Non-Discoverable Mode
+ (See Spec)
+TSPC_GAP_42_2 True (*) Peripheral BR/EDR/LE: Discoverable Mode
+ (See Spec)
+TSPC_GAP_42_3 False Peripheral BR/EDR/LE: Non-Connectable Mode
+ (See Spec)
+TSPC_GAP_42_4 True Peripheral BR/EDR/LE: Connectable Mode (M)
+TSPC_GAP_42_5 True (*) Peripheral BR/EDR/LE: Non-Bondable Mode
+ (See Spec)
+TSPC_GAP_42_6 True (*) Peripheral BR/EDR/LE: Bondable Mode (See Spec)
-------------------------------------------------------------------------------
C.1: Mandatory if TSPC_GAP_1_1 is supported over BR/EDR, otherwise Excluded.
C.2: Mandatory if (TSPC_GAP_1_2 or TSPC_GAP_1_3) is supported over BR/EDR,
@@ -737,7 +741,7 @@
-------------------------------------------------------------------------------
Parameter Name Selected Description
-------------------------------------------------------------------------------
-TSPC_GAP_43_1 False (*) Peripheral BR/EDR/LE: Non-Discoverable Mode
+TSPC_GAP_43_1 True Peripheral BR/EDR/LE: Non-Discoverable Mode
-------------------------------------------------------------------------------
@@ -758,9 +762,9 @@
-------------------------------------------------------------------------------
Parameter Name Selected Description
-------------------------------------------------------------------------------
-TSPC_GAP_45_1 False Simultaneous BR/EDR and LE Transports – BR/EDR
+TSPC_GAP_45_1 True (*) Simultaneous BR/EDR and LE Transports – BR/EDR
Slave to the same device (C.1)
-TSPC_GAP_45_2 False Simultaneous BR/EDR and LE Transports – BR/EDR
+TSPC_GAP_45_2 True (*) Simultaneous BR/EDR and LE Transports – BR/EDR
Master to the same device (C.1)
-------------------------------------------------------------------------------
C.1: Optional if ((SUM ICS 31/14 (Core Spec Version 4.1) or SUM ICS 31/15
diff --git a/android/pics-gatt.txt b/android/pics-gatt.txt
index f3e8199..9cdbe85 100644
--- a/android/pics-gatt.txt
+++ b/android/pics-gatt.txt
@@ -13,8 +13,8 @@
-------------------------------------------------------------------------------
TSPC_GATT_1_1 True Generic Attribute Profile Client (C.1)
TSPC_GATT_1_2 True Generic Attribute Profile Server (C.2)
-TSPC_GATT_1A_1 False (*) Complete GATT client (C.3)
-TSPC_GATT_1A_2 False (*) Complete GATT server (C.4)
+TSPC_GATT_1A_1 True Complete GATT client (C.3)
+TSPC_GATT_1A_2 True Complete GATT server (C.4)
-------------------------------------------------------------------------------
C.1: Optional to support IF TSPC_GATT_2_2; else IF TSPC_GATT_2_1 it is mandatory
to support at least one of TSPC_GATT_1_1 OR TSPC_GATT_1_2
@@ -29,7 +29,7 @@
-------------------------------------------------------------------------------
Parameter Name Selected Description
-------------------------------------------------------------------------------
-TSPC_GATT_2_1 True Attribute Protocol Supported over BR/EDR
+TSPC_GATT_2_1 False (*) Attribute Protocol Supported over BR/EDR
(L2CAP fixed channel support) (C.1)
TSPC_GATT_2_2 True Attribute Protocol Supported over LE (C.2)
-------------------------------------------------------------------------------
@@ -58,7 +58,7 @@
TSPC_GATT_3_8 True Client: Read Characteristic Value (C.1)
TSPC_GATT_3_9 True Client: Read using Characteristic UUID (C.1)
TSPC_GATT_3_10 True Client: Read Long Characteristic Values (C.1)
-TSPC_GATT_3_11 True Client: Read Multiple Characteristic
+TSPC_GATT_3_11 False (*) Client: Read Multiple Characteristic
Values (C.1)
TSPC_GATT_3_12 True Client: Write without Response (C.1)
TSPC_GATT_3_13 True Client: Signed Write Without Response (C.1)
@@ -140,10 +140,10 @@
TSPC_GATT_4_8 True Server: Read Characteristic Value (M)
TSPC_GATT_4_9 True Server: Read using Characteristic UUID (M)
TSPC_GATT_4_10 True Server: Read Long Characteristic Values (C.4)
-TSPC_GATT_4_11 True Server: Read Multiple Characteristic
+TSPC_GATT_4_11 False (*) Server: Read Multiple Characteristic
Values (C.4)
TSPC_GATT_4_12 True Server: Write without Response (C.2)
-TSPC_GATT_4_13 False (*) Server: Signed Write Without Response (C.4)
+TSPC_GATT_4_13 True Server: Signed Write Without Response (C.4)
TSPC_GATT_4_14 True Server: Write Characteristic Value (C.3)
TSPC_GATT_4_15 True Server: Write Long Characteristic Values (C.4)
TSPC_GATT_4_16 True Server: Characteristic Value Reliable
@@ -221,7 +221,7 @@
-------------------------------------------------------------------------------
TSPC_GATT_6_2 True Discover GATT Services using Service Discovery
Profile (C.1)
-TSPC_GATT_6_3 True Publish SDP record for GATT services support
+TSPC_GATT_6_3 False (*) Publish SDP record for GATT services support
via BR/EDR (C.2)
-------------------------------------------------------------------------------
C.1: Mandatory IF TSPC_GATT_1_1 is supported, otherwise Excluded
@@ -239,7 +239,7 @@
TSPC_GATT_7_4 True LE Authentication Procedure (C.2)
TSPC_GATT_7_5 True LE connection data signing procedure (C.2)
TSPC_GATT_7_6 True LE Authenticate signed data procedure (C.2)
-TSPC_GATT_7_7 False (*) LE Authorization Procedure (C.2)
+TSPC_GATT_7_7 True LE Authorization Procedure (C.2)
-------------------------------------------------------------------------------
C.1: Mandatory IF TSPC_GATT_2_1 is supported, otherwise Excluded
C.2: Optional IF TSPC_GATT_2_2 is supported, otherwise Excluded
@@ -251,20 +251,20 @@
Parameter Name Selected Description
-------------------------------------------------------------------------------
TSPC_ATT_3_1 True Attribute Error Response (M)
-TSPC_ATT_3_2 False (*) Exchange MTU Request (O)
-TSPC_ATT_3_4 False (*) Find Information Request (O)
-TSPC_ATT_3_6 False (*) Find by Type Value Request (O)
-TSPC_ATT_3_8 False (*) Read by Type Request (O)
-TSPC_ATT_3_10 False (*) Read Request (O)
-TSPC_ATT_3_12 False (*) Read Blob Request (O)
+TSPC_ATT_3_2 True Exchange MTU Request (O)
+TSPC_ATT_3_4 True Find Information Request (O)
+TSPC_ATT_3_6 True Find by Type Value Request (O)
+TSPC_ATT_3_8 True Read by Type Request (O)
+TSPC_ATT_3_10 True Read Request (O)
+TSPC_ATT_3_12 True Read Blob Request (O)
TSPC_ATT_3_14 False (*) Read Multiple Request (O)
-TSPC_ATT_3_16 False (*) Read by Group Type Request (O)
-TSPC_ATT_3_17 False (*) Read by Group Type Response (C.6)
-TSPC_ATT_3_18 False (*) Write Request (O)
-TSPC_ATT_3_20 False (*) Write Command (O)
-TSPC_ATT_3_21 False (*) Signed Write Command (O)
-TSPC_ATT_3_22 False (*) Prepare Write Request (O)
-TSPC_ATT_3_24 False (*) Execute Write Request (C.8)
+TSPC_ATT_3_16 True Read by Group Type Request (O)
+TSPC_ATT_3_17 True Read by Group Type Response (C.6)
+TSPC_ATT_3_18 True Write Request (O)
+TSPC_ATT_3_20 True Write Command (O)
+TSPC_ATT_3_21 True Signed Write Command (O)
+TSPC_ATT_3_22 True Prepare Write Request (O)
+TSPC_ATT_3_24 True Execute Write Request (C.8)
TSPC_ATT_3_26 True Handle Value Notification (M)
-------------------------------------------------------------------------------
C.6: Mandatory IF TSPC_ATT_3_16 is supported, otherwise Excluded
@@ -284,12 +284,12 @@
TSPC_ATT_4_11 True Read Response (M)
TSPC_ATT_4_15 False (*) Read Multiple Response (C.2)
TSPC_ATT_4_17 True Read by Group Type Response (M)
-TSPC_ATT_4_19 False (*) Write Response (C.3)
-TSPC_ATT_4_20 False (*) Write Command (O)
-TSPC_ATT_4_21 False (*) Signed Write Command (O)
-TSPC_ATT_4_23 False (*) Prepare Write Response (C.4)
-TSPC_ATT_4_25 False (*) Execute Write Response (C.5)
-TSPC_ATT_4_26 False (*) Handle Value Notification (O)
+TSPC_ATT_4_19 True Write Response (C.3)
+TSPC_ATT_4_20 True Write Command (O)
+TSPC_ATT_4_21 True Signed Write Command (O)
+TSPC_ATT_4_23 True Prepare Write Response (C.4)
+TSPC_ATT_4_25 True Execute Write Response (C.5)
+TSPC_ATT_4_26 True Handle Value Notification (O)
-------------------------------------------------------------------------------
C.2: Mandatory IF TSPC_ATT_4_14 is supported, otherwise Excluded
C.3: Mandatory IF TSPC_ATT_4_18 is supported, otherwise Excluded
@@ -302,9 +302,9 @@
-------------------------------------------------------------------------------
Parameter Name Selected Description
-------------------------------------------------------------------------------
-TSPC_ATT_5_2 False (*) LE Security Mode 1 (C.2)
-TSPC_ATT_5_4 False (*) LE Authentication Procedure (C.2)
-TSPC_ATT_5_7 False (*) LE Authorization Procedure (C.2)
+TSPC_ATT_5_2 True LE Security Mode 1 (C.2)
+TSPC_ATT_5_4 True LE Authentication Procedure (C.2)
+TSPC_ATT_5_7 True LE Authorization Procedure (C.2)
-------------------------------------------------------------------------------
C.2: Optional to support if 2/2 (Attribute Protocol Supported over LE),
otherwise Excluded
@@ -315,7 +315,7 @@
-------------------------------------------------------------------------------
Parameter Name Selected Description
-------------------------------------------------------------------------------
-TSPC_GAP_0_2 False (*) LE (C.2)
+TSPC_GAP_0_2 True LE (C.2)
-------------------------------------------------------------------------------
C.2: Mandatory IF (SUM ICS 34/2 (LE GAP) AND NOT SUM ICS 32/3 (BR/EDR GAP))
is supported, otherwise Excluded
diff --git a/android/pics-hdp.txt b/android/pics-hdp.txt
index 3d8020d..95d9917 100644
--- a/android/pics-hdp.txt
+++ b/android/pics-hdp.txt
@@ -47,7 +47,7 @@
TSPC_HDP_2_8 True Supports Acceptance of Bonding requests (M)
TSPC_HDP_2_9 True (*) Supports Initiation of Bonding (O)
TSPC_HDP_2_10 True (*) Supports Extended Inquiry Response (C.7)
-TSPC_HDP_2_11 True (*) Supports use of Health Class of Device (O)
+TSPC_HDP_2_11 False Supports use of Health Class of Device (O)
-------------------------------------------------------------------------------
C.1: Mandatory if TSPC_HDP_2_9 is supported, otherwise Optional.
C.2: Mandatory if Security Mode 2, 3, or 4 is supported, otherwise Optional.
@@ -189,9 +189,9 @@
TSPC_HDP_8_6 True Supports Initiation of Encryption (M) (C.3)
TSPC_HDP_8_7 True Supports General Inquiry (M) C.4)
TSPC_HDP_8_8 True Supports Acceptance of Bonding requests (M)
-TSPC_HDP_8_9 Ture (*) Supports Initiation of Bonding (O)
+TSPC_HDP_8_9 True (*) Supports Initiation of Bonding (O)
TSPC_HDP_8_10 True (*) Supports Extended Inquiry Response (C.5) (C.6)
-TSPC_HDP_8_11 True (*) Supports use of Health Class of Device (O)
+TSPC_HDP_8_11 False Supports use of Health Class of Device (O)
-------------------------------------------------------------------------------
C.1: Mandatory if TSPC_HDP_8_9 is supported, otherwise Optional.
C.2: Mandatory if Security Mode 2, 3, or 4 is supported, otherwise Optional.
diff --git a/android/pics-hogp.txt b/android/pics-hogp.txt
new file mode 100644
index 0000000..81e182c
--- /dev/null
+++ b/android/pics-hogp.txt
@@ -0,0 +1,397 @@
+HOGP PICS for the PTS tool.
+
+PTS version: 5.1
+
+* - different than PTS defaults
+# - not yet implemented/supported
+
+M - mandatory
+O - optional
+
+ Profile Roles
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_HOGP_1_1 False (*) HID Device (C.1)
+TSPC_HOGP_1_2 True Report Host (C.1)
+TSPC_HOGP_1_3 False (*) Boot Host (C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support at least one of TSPC_HOGP_1_1 or TSPC_HOGP_1_2
+ or TSPC_HOGP_1_3.
+-------------------------------------------------------------------------------
+
+
+ Transport
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_HOGP_2_1 False (*) Profile supported over BR/EDR (C.1)
+TSPC_HOGP_2_2 True Profile supported over LE (M)
+-------------------------------------------------------------------------------
+C.1: Excluded for this profile.
+-------------------------------------------------------------------------------
+
+
+ Services - HID Device
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_HOGP_3_1 False (*) Implements HID Service (M.1)
+TSPC_HOGP_3_2 False (*) Multiple Service instances - HID Service (O)
+TSPC_HOGP_3_3 False (*) Implements Battery Service (M.1)
+TSPC_HOGP_3_4 False (*) Implements Device Information Service (M.1)
+TSPC_HOGP_3_5 False (*) Implements Scan Parameters Service (O)
+-------------------------------------------------------------------------------
+M.1: Mandatory if TSPC_HOGP_1_1 selected
+-------------------------------------------------------------------------------
+
+
+ Features - HID Device
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_HOGP_4_1 False (*) Include HID Service UUID in AD in GAP
+ Discoverable Mode (O)
+TSPC_HOGP_4_2 False (*) Include Local Name in AD or Scan Response Data
+ (O)
+TSPC_HOGP_4_3 False (*) Include Appearance in AD or Scan Response Data
+ (O)
+TSPC_HOGP_4_4 False (*) Support Device Information Service
+ characteristic: PnP ID (M)
+TSPC_HOGP_4_5 False (*) Report characteristic (C.1)
+TSPC_HOGP_4_6 False (*) Non-HID Service characteristic described within
+ Report Map characteristic (C.1)
+TSPC_HOGP_4_7 False (*) External Report Reference characteristic
+ descriptor for Report Map characteristic
+ (C.2)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support at least one of these features.
+C.2: Mandatory if TSPC_HOGP_4_6 is supported, else excluded.
+-------------------------------------------------------------------------------
+
+
+ GAP Requirements - HID Device
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_HOGP_5_1 False (*) Peripheral (M.1)
+TSPC_HOGP_5_2 False (*) Directed Connectable Mode (O)
+TSPC_HOGP_5_3 False (*) Undirected Connectable Mode (M.1)
+TSPC_HOGP_5_4 False (*) Bondable mode (peripheral) (M.1)
+TSPC_HOGP_5_5 False (*) Bonding procedure (peripheral) (M.1)
+TSPC_HOGP_5_6 False (*) LE Security Mode 1 (peripheral) (M.1)
+-------------------------------------------------------------------------------
+M.1: Mandatory if TSPC_HOGP_1_1 selected
+-------------------------------------------------------------------------------
+
+
+ SM Requirements - HID Device
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_HOGP_6_1 False (*) SM 2.3.1 (M.1)
+TSPC_HOGP_6_2 False (*) Unauthenticated no MITM protection
+ (LE Security Level 2, Just Works) (M.1)
+TSPC_HOGP_6_3 False (*) Authenticated MITM protection
+ (LE Security Level 3, Passkey) (O)
+-------------------------------------------------------------------------------
+M.1: Mandatory if TSPC_HOGP_1_1 selected
+-------------------------------------------------------------------------------
+
+
+ Client Services Support - Report Host
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_HOGP_7_1 True HID Service (M)
+TSPC_HOGP_7_2 True Battery Service (M)
+TSPC_HOGP_7_3 True Device Information Service (M)
+TSPC_HOGP_7_4 True Scan Parameters Service (M)
+-------------------------------------------------------------------------------
+
+
+ GATT based Profile Support - Report Host
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_HOGP_7a_1 True Scan Parameters Profile (M)
+-------------------------------------------------------------------------------
+
+
+ Client Service Support - Boot Host
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_HOGP_8_1 False (*) HID Service (M.1)
+TSPC_HOGP_8_2 False (*) Battery Service (O)
+TSPC_HOGP_8_3 False (*) Device Information Service (O)
+-------------------------------------------------------------------------------
+M.1: Mandatory if TSPC_HOGP_1_3 selected
+-------------------------------------------------------------------------------
+
+
+ Discover Services & Characteristics - Report Host
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_HOGP_9_1 True Discover HID Service (M)
+TSPC_HOGP_9_2 True Discover Battery Service (M)
+TSPC_HOGP_9_3 True Discover Device Information Service (M)
+TSPC_HOGP_9_4 True Discover Scan Parameters Service (M)
+TSPC_HOGP_9_5 True Discover HID Service characteristic: Report Map
+ (M)
+TSPC_HOGP_9_6 True Discover HID Service characteristic: Report Map
+ - External Report Reference
+ characteristic descriptor (M)
+TSPC_HOGP_9_7 True Discover HID Service characteristic: Report (M)
+TSPC_HOGP_9_8 True Discover HID Service characteristic: Report (M)
+ - Client Characteristic Configuration
+ characteristic descriptor (M)
+TSPC_HOGP_9_9 True Discover HID Service characteristic: Report
+ - Report Reference characteristic
+ descriptor (M)
+TSPC_HOGP_9_10 True Discover HID Service characteristic: HID
+ Information (M)
+TSPC_HOGP_9_11 True Discover HID Service characteristic: HID
+ Control Point (M)
+TSPC_HOGP_9_12 True Discover HID Service characteristic: Protocol
+ Mode (O)
+TSPC_HOGP_9_13 True Discover Battery Service characteristic: Battery
+ Level (M)
+TSPC_HOGP_9_14 True Discover Battery Service characteristic: Battery
+ Level - Client Characteristic
+ Configuration characteristic descriptor
+ (M)
+TSPC_HOGP_9_15 True Discover Device Information Service
+ characteristic: PnP ID (M)
+TSPC_HOGP_9_16 True Discover non-HID Service characteristic: Report
+ Reference characteristic descriptor (M)
+-------------------------------------------------------------------------------
+
+
+ Discover Services & Characteristics - Boot Host
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_HOGP_10_1 False (*) Discover HID Service (M.1)
+TSPC_HOGP_10_2 False (*) Discover Battery Service (O)
+TSPC_HOGP_10_3 False (*) Discover Device Information Service (O)
+TSPC_HOGP_10_4 False (*) Discover HID Service characteristic: Protocol
+ Mode (M.1)
+TSPC_HOGP_10_5 False (*) Discover HID Service characteristic: Boot
+ Keyboard Input Report (C.1, C.2)
+TSPC_HOGP_10_6 False (*) Discover HID Service characteristic: Boot
+ Keyboard Input Report - Client
+ Characteristic Configuration
+ characteristic descriptor (C.3)
+TSPC_HOGP_10_7 False (*) Discover HID Service characteristic: Boot
+ Keyboard Output Report (C.1, C.2)
+TSPC_HOGP_10_8 False (*) Discover HID Service characteristic: Boot
+ Mouse Input Report (C.1)
+TSPC_HOGP_10_9 False (*) Discover HID Service characteristic: Boot
+ Mouse Input Report - Client
+ Characteristic Configuration
+ characteristic descriptor (C.4)
+TSPC_HOGP_10_10 False (*) Discover Battery Service characteristic:
+ Battery Level (O)
+TSPC_HOGP_10_11 False (*) Discover Battery Service characteristic:
+ Battery Level - Client Characteristic
+ Configuration characteristic descriptor
+ (O)
+TSPC_HOGP_10_12 False (*) Discover Device Information Service
+ characteristic: PnP ID (O)
+-------------------------------------------------------------------------------
+M.1: Mandatory if TSPC_HOGP_1_3 selected
+C.1: Mandatory to support at least one of TSPC_HOGP_10_5, TSPC_HOGP_10_7, or
+ TSPC_HOGP_10_8.
+C.2: If one of TSPC_HOGP_10_5 or TSPC_HOGP_10_7 is supported, both shall be
+ supported.
+C.3: Mandatory to support if TSPC_HOGP_10_5 is supported, else excluded.
+C.4: Mandatory to support if TSPC_HOGP_10_8 is supported, else excluded.
+-------------------------------------------------------------------------------
+
+
+ Features - Report Host
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_HOGP_11_1 True Read Report Map characteristic (M)
+TSPC_HOGP_11_2 True Read Report Map characteristic: External
+ Report Reference characteristic
+ descriptor (M)
+TSPC_HOGP_11_3 True Read Report characteristic: Report Type:
+ Input Report (M)
+TSPC_HOGP_11_4 True Write Report characteristic: Report Type:
+ Input Report (M)
+TSPC_HOGP_11_5 True Read Report characteristic: Report Type:
+ Output Report (M)
+TSPC_HOGP_11_6 True Write HID Report characteristic: Report Type:
+ Output Report (M)
+TSPC_HOGP_11_7 True Read HID Report characteristic: Report Type:
+ Feature Report (M)
+TSPC_HOGP_11_8 True Write HID Report characteristic: Report Type:
+ Feature Report (M)
+TSPC_HOGP_11_9 True Read Report characteristic: Report
+ Reference characteristic descriptor (M)
+TSPC_HOGP_11_10 True Read Report characteristic: Input Report:
+ Client Characteristic Configuration
+ characteristic descriptor (M)
+TSPC_HOGP_11_11 True Report characteristic configuration with 0x0001
+ (M)
+
+TSPC_HOGP_11_11a True Report characteristic configuration with 0x0000
+ (?)
+
+TSPC_HOGP_11_12 True Read HID Information characteristic
+ (M)
+TSPC_HOGP_11_13 True Suspend State (O)
+TSPC_HOGP_11_14 True Exit Suspend State (C.1)
+TSPC_HOGP_11_15 True Write HID Control Point characteristic: Suspend
+ command (C.1)
+TSPC_HOGP_11_16 True Write HID Control Point characteristic: Exit
+ Suspend command (C.1)
+TSPC_HOGP_11_17 True Read Protocol Mode characteristic: Get Protocol
+ command (O)
+TSPC_HOGP_11_18 True Write Protocol Mode characteristic: Set Report
+ Protocol Mode command (O)
+TSPC_HOGP_11_19 True Read Battery Level characteristic (M)
+TSPC_HOGP_11_20 True Read Battery Level characteristic: Client
+ Characteristic Configuration
+ characteristic descriptor (M)
+TSPC_HOGP_11_21 True Battery Level characteristic configuration with
+ 0x0000 0r 0x0001 (M)
+TSPC_HOGP_11_22 True Read non-HID Service characteristic: Report
+ Reference characteristic descriptor (M)
+TSPC_HOGP_11_23 True Read PnP ID characteristic (M)
+TSPC_HOGP_11_24 True Notify Report characteristic (M)
+TSPC_HOGP_11_25 True Notify Battery Level characteristic (M)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support if TSPC_HOGP_11_13 is supported, else excluded.
+-------------------------------------------------------------------------------
+
+
+ Features - Boot Host
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_HOGP_12_1 False (*) Read Protocol Mode characteristic: Get Protocol
+ Mode command (M.1)
+TSPC_HOGP_12_2 False (*) Write Protocol Mode characteristic: Set Boot
+ Protocol Mode command (M.1)
+TSPC_HOGP_12_3 False (*) Read HID Service characteristic: Boot Keyboard
+ Input Report (C.1)
+TSPC_HOGP_12_4 False (*) Write HID Service characteristic: Boot Keyboard
+ Input Report (C.1)
+TSPC_HOGP_12_5 False (*) Read Client Characteristic Configuration
+ characteristic descriptor for Boot
+ Keyboard Input Report (C.1)
+TSPC_HOGP_12_6 False (*) Boot Keyboard Input Report characteristic:
+ configuration with 0x0000 or 0x0001
+ (C.1)
+TSPC_HOGP_12_7 False (*) Read HID Service characteristic: Boot Keyboard
+ Output Report (C.1)
+TSPC_HOGP_12_8 False (*) Write HID Service characteristic: Boot Keyboard
+ Output Report (C.1)
+TSPC_HOGP_12_9 False (*) Read HID Service characteristic: Boot Mouse
+ Input Report (C.2)
+TSPC_HOGP_12_10 False (*) Write HID Service characteristic: Boot Mouse
+ Input Report (C.2)
+TSPC_HOGP_12_11 False (*) Read Client Characteristic Configuration
+ characteristic descriptor for Boot
+ Mouse Input Report (C.2)
+TSPC_HOGP_12_12 False (*) Boot Mouse Input Report characteristic:
+ configuration with 0x0000 or 0x0001
+ (C.2)
+TSPC_HOGP_12_13 False (*) Notify Boot Keyboard Input Report characteristic
+ (C.1)
+TSPC_HOGP_12_14 False (*) Notify Boot Mouse Input Report characteristic
+ (C.2)
+TSPC_HOGP_12_15 False (*) Read Battery Level characteristic (O)
+TSPC_HOGP_12_16 False (*) Read Battery Level characteristic: Client
+ Characteristic Configuration
+ characteristic descriptor (O)
+TSPC_HOGP_12_17 False (*) Battery Level characteristic: configuration with
+ 0x0000 or 0x0001 (O)
+TSPC_HOGP_12_18 False (*) Notify Battery Level characteristic (O)
+TSPC_HOGP_12_19 False (*) Read PnP ID characteristic (O)
+-------------------------------------------------------------------------------
+M.1: Mandatory if TSPC_HOGP_1_3 selected
+C.1: Mandatory to support if TSPC_HOGP_12_5 or TSPC_HOGP_12_7 is supported,
+ else excluded.
+C.2: Mandatory to support if TSPC_HOGP_12_8 is supported, else excluded.
+-------------------------------------------------------------------------------
+
+
+ GATT Requirements - Report Host
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_HOGP_13_1 True Attribute Protocol supported over LE Transport
+ (M)
+TSPC_HOGP_13_2 True Generic Attribute Profile Client (M)
+TSPC_HOGP_13_3 True Discover All Primary Services (C.1)
+TSPC_HOGP_13_4 True Discover Primary Services by Service UUID (C.1)
+TSPC_HOGP_13_5 True Find Included Services (M)
+TSPC_HOGP_13_6 True Discover All Characteristics of a Service (C.2)
+TSPC_HOGP_13_7 True Discover Characteristics by UUID (C.2)
+TSPC_HOGP_13_8 True Discover All Characteristic Descriptors (M)
+TSPC_HOGP_13_9 True Read Characteristic Value (M)
+TSPC_HOGP_13_10 True Read using Characteristic UUID (O)
+TSPC_HOGP_13_11 True Read Long Characteristic Value (M)
+TSPC_HOGP_13_12 True Read Characteristic Descriptors (M)
+TSPC_HOGP_13_13 True Write without Response (M)
+TSPC_HOGP_13_14 True Write Characteristic Value (M)
+TSPC_HOGP_13_15 True Write Characteristic Descriptors (M)
+TSPC_HOGP_13_16 True Notifications (M)
+TSPC_HOGP_13_17 True Exchange MTU (M)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support at least one of these features.
+C.2: Mandatory to support at least one of these features.
+-------------------------------------------------------------------------------
+
+
+ GATT Requirements - Boot Host
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_HOGP_14_1 False (*) Attribute Protocol supported over LE Transport
+ (M.1)
+TSPC_HOGP_14_2 False (*) Generic Attribute Profile Client (M.1)
+TSPC_HOGP_14_3 False (*) Discover All Primary Services (C.1)
+TSPC_HOGP_14_4 False (*) Discover Primary Services by Service UUID (C.1)
+TSPC_HOGP_14_5 False (*) Discover All Characteristics of a Service (O)
+TSPC_HOGP_14_6 False (*) Discover Characteristics by UUID (O)
+TSPC_HOGP_14_7 False (*) Discover All Characteristic Descriptors (M.1)
+TSPC_HOGP_14_8 False (*) Read Characteristic Value (M.1)
+TSPC_HOGP_14_9 False (*) Read using Characteristic UUID (M.1)
+TSPC_HOGP_14_10 False (*) Read Characteristic Descriptors (M.1)
+TSPC_HOGP_14_11 False (*) Write without Response (M.1)
+TSPC_HOGP_14_12 False (*) Write Characteristic Value (M.1)
+TSPC_HOGP_14_13 False (*) Write Characteristic Descriptors (M.1)
+TSPC_HOGP_14_14 False (*) Notifications (M.1)
+-------------------------------------------------------------------------------
+M.1: Mandatory if TSPC_HOGP_1_3 selected
+C.1: Mandatory to support at least one of these features.
+-------------------------------------------------------------------------------
+
+
+ GAP Requirements - HID Host
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_HOGP_15_1 True Central (M)
+TSPC_HOGP_15_2 True LE Security Mode 1 (central) (M)
+-------------------------------------------------------------------------------
+
+
+ SM Requirements - HID Host
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_HOGP_16_1 True No Security Requirements (LE Security Level 1,
+ No Security) (M)
+TSPC_HOGP_16_2 True Unauthenticated no MITM protection (LE Security
+ Level 2, Just Works) (M)
+TSPC_HOGP_16_3 True Authenticated MITM protection (LE Security
+ Level 3, Passkey) (O)
+-------------------------------------------------------------------------------
diff --git a/android/pics-l2cap.txt b/android/pics-l2cap.txt
index 096fbf4..5cc9d06 100644
--- a/android/pics-l2cap.txt
+++ b/android/pics-l2cap.txt
@@ -70,21 +70,21 @@
TSPC_L2CAP_2_32 False ERTM over AMP (C.12)
TSPC_L2CAP_2_33 False Streaming Mode Source over AMP Support (C.15)
TSPC_L2CAP_2_34 False Streaming Mode Sink over AMP Support (C.15)
-TSPC_L2CAP_2_35 False Unicast Connectionless Data, Reception (C.1, C.16)
-TSPC_L2CAP_2_36 False Ability to transmit an unencrypted packet over
+TSPC_L2CAP_2_35 True (*) Unicast Connectionless Data, Reception (C.1, C.16)
+TSPC_L2CAP_2_36 True (*) Ability to transmit an unencrypted packet over
a Unicast connectionless L2CAP
channel (C.16)
-TSPC_L2CAP_2_37 False Ability to transmit an encrypted packet over
+TSPC_L2CAP_2_37 True (*) Ability to transmit an encrypted packet over
a Unicast connectionless L2CAP
channel (C.16)
-TSPC_L2CAP_2_38 False Extended Flow Specification for BR/EDR (C.8)
-TSPC_L2CAP_2_39 False Extended Window Size (C.8)
+TSPC_L2CAP_2_38 True (*) Extended Flow Specification for BR/EDR (C.8)
+TSPC_L2CAP_2_39 True (*) Extended Window Size (C.8)
TSPC_L2CAP_2_40 True (*) Support of Low Energy signaling channel (C.17)
TSPC_L2CAP_2_41 True (*) Support of command reject (C.17)
TSPC_L2CAP_2_42 True (*) Send Connection Parameter Update Request (C.18)
TSPC_L2CAP_2_43 True (*) Send Connection Parameter Update Response (C.19)
TSPC_L2CAP_2_44 False Extended Flow Specification for AMP (C.22)
-TSPC_L2CAP_2_45 False Send disconnect request command (O)
+TSPC_L2CAP_2_45 True (*) Send disconnect request command (O)
-------------------------------------------------------------------------------
C.1: Mandatory to support at least one of TSPC_L2CAP_2_12 OR TSPC_L2CAP_2_13 OR
TSPC_L2CAP_2_35 IF BR/EDR BR/EDR/LE AND SUM_ICS 31/7 (CSA1) OR
diff --git a/android/pics-mps.txt b/android/pics-mps.txt
index e12cad0..5cbe1d8 100644
--- a/android/pics-mps.txt
+++ b/android/pics-mps.txt
@@ -24,8 +24,8 @@
TSPC_MPS_1_2 True (*) AVRCP 1.3 or later (O)
TSPC_MPS_1_3 False DUN 1.1 or later (O)
TSPC_MPS_1_4 True (*) HFP 1.5 or later (O)
-TSPC_MPS_1_5 False PAN 1.0 or later (O)
-TSPC_MPS_1_6 False PBAP 1.1 or later (O)
+TSPC_MPS_1_5 True (*) PAN 1.0 or later (O)
+TSPC_MPS_1_6 True (*) PBAP 1.1 or later (O)
-------------------------------------------------------------------------------
@@ -41,11 +41,11 @@
TSPC_MPS_2_6 False DUN Data Terminal (DT) (C.1)
TSPC_MPS_2_7 True (*) HFP Audio Gateway (AG) (C.1)
TSPC_MPS_2_8 False HFP Hands-Free (HF) (C.1)
-TSPC_MPS_2_9 False PAN Network Access Point (NAP) (C.1)
+TSPC_MPS_2_9 True (*) PAN Network Access Point (NAP) (C.1)
TSPC_MPS_2_10 False PAN Group Ad-hoc Network (GN) (C.1)
-TSPC_MPS_2_11 False PAN User (PANU) (C.1)
+TSPC_MPS_2_11 True (*) PAN User (PANU) (C.1)
TSPC_MPS_2_12 False PBAP PCE (C.1)
-TSPC_MPS_2_13 False PBAP PSE (C.1)
+TSPC_MPS_2_13 True (*) PBAP PSE (C.1)
-------------------------------------------------------------------------------
C.1: Mandatory to declare each role as supported within the represented Profile
otherwise Excluded. The roles declared shall match that of the roles
@@ -77,10 +77,10 @@
the HF (C.6)
TSPC_MPS_3_10 True (*) Register Notification: PLAYBACK_STATUS_CHANGED
(C.7)
-TSPC_MPS_3_11 False Ability to support parallel data and call
+TSPC_MPS_3_11 True (*) Ability to support parallel data and call
operation (O)
-TSPC_MPS_3_12 False PBAP Phone Book Download (C.8)
-TSPC_MPS_3_13 False Ability to support multiple concurrent device
+TSPC_MPS_3_12 True (*) PBAP Phone Book Download (C.8)
+TSPC_MPS_3_13 True (*) Ability to support multiple concurrent device
connections (O)
-------------------------------------------------------------------------------
C.1: Mandatory if TSPC_MPS_2_1 (A2DP Source role) and TSPC_MPS_2_4 (AVRCP
@@ -109,7 +109,7 @@
Parameter Name Selected Description
-------------------------------------------------------------------------------
TSPC_MPS_4_1 True Multiple Profiles Single Device (MPSD) (M)
-TSPC_MPS_4_2 False Multiple Profiles Multiple Devices (MPMD) (C.1)
+TSPC_MPS_4_2 True (*) Multiple Profiles Multiple Devices (MPMD) (C.1)
-------------------------------------------------------------------------------
C.1: Mandatory if TSPC_MPS_3_13 (Ability to support multiple concurrent device
connections), otherwise Excluded.
@@ -184,41 +184,41 @@
TSPC_MPS_6_26 False HFP-HF and DUN-DT Implementation Terminate
Voice Call/Data Call during Data Communication
and Voice Call (C.6)
-TSPC_MPS_6_27 False HFP-AG and PAN-NAP Implementation Data
+TSPC_MPS_6_27 True (*) HFP-AG and PAN-NAP Implementation Data
Communication in Personal Area Network during
Active Voice Call (C.7)
TSPC_MPS_6_28 False HFP-HF and PAN-PANU Implementation Data
Communication in Personal Area Network during
Active Voice Call (C.8)
-TSPC_MPS_6_29 False HFP-AG and PAN-NAP Implementation Outgoing
+TSPC_MPS_6_29 True (*) HFP-AG and PAN-NAP Implementation Outgoing
Voice Call during Data Communication in Personal
Area Network (C.7)
TSPC_MPS_6_30 False HFP-HF and PAN-PANU Implementation Outgoing
Voice Call during Data Communication in Personal
Area Network (C.8)
-TSPC_MPS_6_31 False HFP-AG and PAN-NAP Implementation Incoming Voice
+TSPC_MPS_6_31 True (*) HFP-AG and PAN-NAP Implementation Incoming Voice
Call during Data Communication in Personal Area
Network (C.7)
TSPC_MPS_6_32 False HFP-HF and PAN-PANU Implementation Incoming
Voice Call during Data Communication in Personal
Area Network (C.8)
-TSPC_MPS_6_33 False A2DP-SRC and PAN-NAP Implementation Start Audio
+TSPC_MPS_6_33 True (*) A2DP-SRC and PAN-NAP Implementation Start Audio
Streaming during Data Communication in Personal
Area Network (C.9)
TSPC_MPS_6_34 False A2DP-SNK and PAN-PANU Implementation Start Audio
Streaming during Data Communication in Personal
Area Network (C.10)
-TSPC_MPS_6_35 False A2DP-SRC and PAN-NAP Implementation Data
+TSPC_MPS_6_35 True (*) A2DP-SRC and PAN-NAP Implementation Data
Communication Establishment in Personal Area
Network during Audio Streaming (C.9)
TSPC_MPS_6_36 False A2DP-SNK and PAN_PANU Implementation Data
Communication Establishment in Personal Area
Network during Audio Streaming (C.10)
-TSPC_MPS_6_37 False A2DP-SRC_PBAP-Server Implementation Phonebook
+TSPC_MPS_6_37 True (*) A2DP-SRC_PBAP-Server Implementation Phonebook
Download during Audio Streaming (C.11)
TSPC_MPS_6_38 False A2DP-SNK and PBAP-Client Implementation
Phonebook Download during Audio Streaming (C.12)
-TSPC_MPS_6_39 False HFP-AG and PBAP-Server Implementation PBAP and
+TSPC_MPS_6_39 True (*) HFP-AG and PBAP-Server Implementation PBAP and
HFP Connection Behavior (C.13)
-------------------------------------------------------------------------------
C.1: Mandatory if 2/1 (A2DP Source role), 2/4 (AVRCP Target role) and 2/7
diff --git a/android/pics-sm.txt b/android/pics-sm.txt
index 3060d40..239f568 100644
--- a/android/pics-sm.txt
+++ b/android/pics-sm.txt
@@ -25,7 +25,7 @@
TSPC_SM_2_1 True Authenticated MITM protection (O)
TSPC_SM_2_2 True Unauthenticated no MITM protection (C.1)
TSPC_SM_2_3 True No security requirements (M)
-TSPC_SM_2_4 True OOB supported (O)
+TSPC_SM_2_4 False OOB supported (O)
-------------------------------------------------------------------------------
C.1: If TSPC_SM_2_1 is supported then Mandatory, else Optional
-------------------------------------------------------------------------------
@@ -45,7 +45,7 @@
-------------------------------------------------------------------------------
TSPC_SM_4_1 True Just Works (O)
TSPC_SM_4_2 True Passkey Entry (C.1)
-TSPC_SM_4_3 True Out of Band (C.1)
+TSPC_SM_4_3 False Out of Band (C.1)
-------------------------------------------------------------------------------
C.1: If TSPC_SM_2_1 is supported, at least one of these features shall be
supported.
diff --git a/android/pixit-hogp.txt b/android/pixit-hogp.txt
new file mode 100644
index 0000000..4fe1e0f
--- /dev/null
+++ b/android/pixit-hogp.txt
@@ -0,0 +1,28 @@
+HOGP PIXIT for the PTS tool.
+
+PTS version: 5.1
+
+* - different than PTS defaults
+& - should be set to IUT Bluetooth address
+
+ Required PIXIT settings
+-------------------------------------------------------------------------------
+Parameter Name Value
+-------------------------------------------------------------------------------
+TSPX_bd_addr_iut 112233445566 (*&)
+TSPX_time_guard 180000
+TSPX_use_implicit_send TRUE
+TSPX_tester_database_file [Path to HOGP Test
+ Database]
+TSPX_mtu_size 23
+TSPX_secure_simple_pairing_pass_key_confirmation FALSE
+TSPX_delete_link_key FALSE
+TSPX_pin_code 0000
+TSPX_use_dynamic_pin FALSE
+TSPX_delete_ltk FALSE
+TSPX_security_enabled TRUE
+TSPX_input_report_data CDA6F8B3AA
+TSPX_output_report_data 001234567890EF
+TSPX_feature_report_data 872D3F45EA
+TSPX_tester_appearance 03C0
+-------------------------------------------------------------------------------
diff --git a/android/pixit-mps.txt b/android/pixit-mps.txt
index c59c913..e3c8096 100644
--- a/android/pixit-mps.txt
+++ b/android/pixit-mps.txt
@@ -10,11 +10,11 @@
-------------------------------------------------------------------------------
Parameter Name Value
-------------------------------------------------------------------------------
-TSPX_avrcp_revision
+TSPX_avrcp_revision 1.5 (^)
TSPX_class_of_device 20050C
TSPX_establish_avdtp_stream TRUE
TSPX_iut_establishes_initial_condition FALSE
-TSPX_tester_device
+TSPX_tester_device A (*)
TSPX_media_directory
TSPX_bd_addr_iut 112233445566 (*&)
TSPX_delete_link_key FALSE
diff --git a/android/pts-a2dp.txt b/android/pts-a2dp.txt
index 3670dd0..5780db1 100644
--- a/android/pts-a2dp.txt
+++ b/android/pts-a2dp.txt
@@ -1,7 +1,7 @@
PTS test results for A2DP
PTS version: 5.1
-Tested: 07-April-2014
+Tested: 06-June-2014
Android version: 4.4.2
Results:
diff --git a/android/pts-gap.txt b/android/pts-gap.txt
index d1f0402..02430f4 100644
--- a/android/pts-gap.txt
+++ b/android/pts-gap.txt
@@ -1,8 +1,9 @@
PTS test results for GAP
PTS version: 5.1
-Tested: 16-May-2014
+Tested: 28-May-2014
Android version: 4.4.2
+Kernel version: 3.17
Results:
PASS test passed
@@ -29,16 +30,41 @@
TC_BROB_OBSV_BV_03_C N/A
TC_BROB_OBSV_BV_04_C N/A
TC_BROB_OBSV_BV_05_C N/A
-TC_DISC_NONM_BV_01_C N/A
-TC_DISC_NONM_BV_02_C N/A
-TC_DISC_LIMM_BV_01_C N/A
-TC_DISC_LIMM_BV_02_C N/A
-TC_DISC_LIMM_BV_03_C N/A
-TC_DISC_LIMM_BV_04_C N/A
-TC_DISC_GENM_BV_01_C N/A
-TC_DISC_GENM_BV_02_C N/A
-TC_DISC_GENM_BV_03_C N/A
-TC_DISC_GENM_BV_04_C N/A
+TC_DISC_NONM_BV_01_C PASS btmgmt connectable off
+ btmgmt advertising on
+TC_DISC_NONM_BV_02_C PASS btmgmt connectable on
+ btmgmt discov off
+ btmgmt advertising on
+TC_DISC_LIMM_BV_01_C PASS btmgmt connectable on
+ btmgmt discov off
+ <answer NO to non-connectable adv question>
+ btmgmt discov limited 30
+TC_DISC_LIMM_BV_02_C PASS btmgmt connectable on
+ btmgmt advertising on
+ btmgmt discov limited 30
+TC_DISC_LIMM_BV_03_C PASS btmgmt connectable on
+ btmgmt discov off
+ <answer NO to non-connectable adv question>
+ btmgmt discov limited 30
+TC_DISC_LIMM_BV_04_C PASS btmgmt connectable on
+ btmgmt discov off
+ btmgmt power off
+ btmgmt bredr off
+ btmgmt power on
+ btmgmt discov limited 30
+TC_DISC_GENM_BV_01_C PASS btmgmt connectable on
+ btmgmt discov on
+ <answer NO to non-connectable adv question>
+TC_DISC_GENM_BV_02_C PASS btmgmt connectable on
+ btmgmt advertising on
+ btmgmt discov on
+TC_DISC_GENM_BV_03_C PASS btmgmt connectable on
+ btmgmt discov on
+TC_DISC_GENM_BV_04_C PASS btmgmt connectable on
+ btmgmt power off
+ btmgmt bredr off
+ btmgmt discov on
+ btmgmt advertising on
TC_DISC_LIMP_BV_01_C PASS btmgmt find -l
PTS AD flags must have bit 1 unset and bit 0 set
TC_DISC_LIMP_BV_02_C PASS btmgmt find -l
@@ -66,88 +92,258 @@
all services, get characteristic and then read
characteristic (name)
TC_IDLE_NAMP_BV_02_C PASS haltest: gatts connect
-TC_CONN_NCON_BV_01_C N/A
-TC_CONN_NCON_BV_02_C N/A
-TC_CONN_NCON_BV_03_C N/A
-TC_CONN_DCON_BV_01_C N/A
+TC_CONN_NCON_BV_01_C PASS btmgmt connectable off
+ btmgmt advertising on
+TC_CONN_NCON_BV_02_C PASS <answer NO to non-connectable adv question>
+ Note: non-connectable and discoverable ?
+TC_CONN_NCON_BV_03_C PASS <answer NO to non-connectable adv question>
+ Note: non-connectable and discoverable ?
+TC_CONN_DCON_BV_01_C PASS btmgmt connectable on
+ btmgmt advertising on
TC_CONN_DCON_BV_02_C N/A
TC_CONN_DCON_BV_03_C N/A
-TC_CONN_UCON_BV_01_C N/A
-TC_CONN_UCON_BV_02_C N/A
-TC_CONN_UCON_BV_03_C N/A
+TC_CONN_UCON_BV_01_C PASS btmgmt connectable on
+ btmgmt advertising on
+TC_CONN_UCON_BV_02_C PASS btmgmt connectable on
+ btmgmt discov on
+ btmgmt advertising on
+TC_CONN_UCON_BV_03_C PASS btmgmt connectable on
+ btmgmt advertising on
+ btmgmt discov limited 30
TC_CONN_UCON_BV_04_C N/A
TC_CONN_UCON_BV_05_C N/A
-TC_CONN_ACEP_BV_01_C PASS 'gattc connect' prior to pressing OK on PTS
-TC_CONN_ACEP_BV_02_C INC Privacy feature - not implemented
+TC_CONN_ACEP_BV_01_C PASS debugfs:
+ echo -n "add <PTS addr> 0 1" > /sys/kernel/
+ debug/bluetooth/hciX/le_auto_conn
+ btmgmt le on
+ btmgmt power on
+ btmgmt disconnect -t 1 <PTS addr>
+ debugfs:
+ echo -n "clr" > /sys/kernel/debug/bluetooth/
+ hciX/le_auto_conn
+TC_CONN_ACEP_BV_02_C INC Privacy feature - PTS issue #12308
TC_CONN_GCEP_BV_01_C PASS 'gattc connect' prior to pressing OK on PTS
TC_CONN_GCEP_BV_02_C PASS 'gattc connect' prior to pressing OK on PTS
-TC_CONN_GCEP_BV_03_C INC Privacy feature - not implemented
-TC_CONN_GCEP_BV_04_C INC Privacy feature - not implemented
+TC_CONN_GCEP_BV_03_C PASS gattc connect
+ bluetooth create_bond
+ gattc connect
+ gattc disconnect
+TC_CONN_GCEP_BV_04_C INC Privacy feature - PTS issue #12308
TC_CONN_SCEP_BV_01_C PASS 'gattc connect' prior to pressing OK on PTS
-TC_CONN_SCEP_BV_02_C INC Privacy feature - not implemented
+TC_CONN_SCEP_BV_02_C INC Privacy feature - PTS issue #12308
TC_CONN_DCEP_BV_01_C PASS 'gattc connect' prior to pressing OK on PTS
-TC_CONN_DCEP_BV_02_C INC Privacy feature - not implemented
+TC_CONN_DCEP_BV_02_C INC Privacy feature - PTS issue #12308
TC_CONN_DCEP_BV_03_C PASS
-TC_CONN_DCEP_BV_04_C INC Privacy feature - not implemented
+TC_CONN_DCEP_BV_04_C PASS gattc connect
+ gattc create_bond
+ gattc connect
+ gattc disconnect
TC_CONN_CPUP_BV_01_C N/A
TC_CONN_CPUP_BV_02_C N/A
TC_CONN_CPUP_BV_03_C N/A
-TC_CONN_CPUP_BV_04_C PASS
-TC_CONN_CPUP_BV_05_C PASS
-TC_CONN_CPUP_BV_06_C PASS PTS issue - ID: 12187
- ETS + new pixit file should be added
+TC_CONN_CPUP_BV_04_C PASS gattc register_client
+ gattc connect 1 <pts_bdaddr>
+ gattc disconnect 1 <pts_bdaddr> 1
+TC_CONN_CPUP_BV_05_C PASS gattc register_client
+ gattc connect 1 <pts_bdaddr>
+ gattc disconnect 1 <pts_bdaddr> 1
+TC_CONN_CPUP_BV_06_C PASS gattc register_client
+ gattc connect 1 <pts_bdaddr>
+ hcitool lecup <handle> 0x0008 0x0C7B 0x0010
+ 0x0014
+ gattc disconnect <client_if> <pts_bdaddr>
+ <conn_id>
TC_CONN_TERM_BV_01_C PASS
-TC_CONN_PRDA_BV_01_C N/A
-TC_CONN_PRDA_BV_02_C INC Privacy feature - not implemented
+TC_CONN_PRDA_BV_01_C INC PTS issue #12207
+TC_CONN_PRDA_BV_02_C INC PTS issue #12310
TC_BOND_NBON_BV_01_C PASS
TC_BOND_NBON_BV_02_C PASS haltest: gattc_register_client
gattc connect <client_id> <address>
bluetooth create_bond <address>
bluetooth remove_bond <address>
-TC_BOND_NBON_BV_03_C N/A
-TC_BOND_BON_BV_01_C N/A
+TC_BOND_NBON_BV_03_C PASS
+TC_BOND_BON_BV_01_C PASS gattc register_client
+ gattc listen 1
+ bluetooth create_bond <pts_bdaddr>
TC_BOND_BON_BV_02_C PASS
-TC_BOND_BON_BV_03_C N/A
+TC_BOND_BON_BV_03_C PASS haltest: bluetooth remove_bond <pts_bdaddr>
+ gattc_register_client
+ gattc listen 1
TC_BOND_BON_BV_04_C PASS haltest: gattc_register_client
gattc connect <client_id> <address>
bluetooth remove_bond <address>
gattc connect <client_id> <address>
-TC_SEC_AUT_BV_11_C N/A
-TC_SEC_AUT_BV_12_C INC JIRA: BZ-29
-TC_SEC_AUT_BV_13_C INC JIRA: BZ-30
-TC_SEC_AUT_BV_14_C N/A
+TC_SEC_AUT_BV_11_C PASS haltest:gattc register_client
+ gatts register_server
+ gatts add_service 2 <uuid> 3
+ gatts add_characteristic 2 1b <uuid> 10 68
+ gatts start_service 2 1b 1
+ gattc listen
+ PTS asks for handle with Insufficient auth
+ bluetooth ssp_reply <addr> <passkey>
+ gatts send_response
+TC_SEC_AUT_BV_12_C PASS haltest: gatts register_server
+ gatts add_service 2 <uuid> 3
+ gatts add_characteristic 2 <service_handle>
+ <uuid> 10 68
+ gatts start_service 2 <service_handle> 1
+ gatts connect <server_if> <addr>
+ PTS asks for handle with Insufficient auth
+ bluetooth ssp_reply <addr> <passkey>
+ gatts send_response
+TC_SEC_AUT_BV_13_C PASS haltest: gatts register_server
+ gatts add_service 2 <uuid> 3
+ gatts add_characteristic 2 <service_handle>
+ <uuid> 10 68
+ gatts start_service 2 <service_handle> 1
+ gatts connect <server_if> <addr>
+ PTS asks for handle with Insufficient auth
+ bluetooth ssp_reply <addr> <passkey>
+ gatts send_response
+TC_SEC_AUT_BV_14_C PASS haltest:gattc register_client
+ gatts register_server
+ gatts add_service 2 <uuid> 3
+ gatts add_characteristic 2 1b <uuid> 10 68
+ gatts start_service 2 1b 1
+ gattc listen
+ PTS asks for handle with Insufficient auth
+ bluetooth ssp_reply <addr> <passkey>
+ gatts send_response
TC_SEC_AUT_BV_15_C N/A
-TC_SEC_AUT_BV_16_C INC Not implemented
+TC_SEC_AUT_BV_16_C PASS haltest: gatts register_server
+ gatts add_service 2 <uuid> 3
+ gatts add_characteristic 2 <service_handle>
+ <uuid> 10 34
+ gatts start_service 2 <service_handle> 1
+ gatts connect <server_if> <addr>
+ gatts disconnect <server_if> <addr>
+ gatts connect <server_if> <addr>
+ PTS asks for handle with Insufficient encrypt
+ bluetooth ssp_reply <addr> <passkey>
+ gatts send_response
TC_SEC_AUT_BV_17_C PASS
-TC_SEC_AUT_BV_18_C N/A
+TC_SEC_AUT_BV_18_C PASS haltest: gattc register_client
+ gattc listen
+ gattc search_service
+ gattc get_characteristic
+ gattc read_characteristic
+ bluetooth create_bond
+ gattc read_characteristic
TC_SEC_AUT_BV_19_C PASS
-TC_SEC_AUT_BV_20_C N/A
-TC_SEC_AUT_BV_21_C INC Not implemented
-TC_SEC_AUT_BV_22_C N/A
-TC_SEC_AUT_BV_23_C N/A
-TC_SEC_AUT_BV_24_C INC Not implemented
-TC_SEC_CSIGN_BV_01_C INC Not yet implemented - signed write
-TC_SEC_CSIGN_BV_02_C INC Not implemented
-TC_SEC_CSIGN_BI_01_C INC Not implemented
-TC_SEC_CSIGN_BI_02_C INC Not implemented
-TC_SEC_CSIGN_BI_03_C INC Not implemented
-TC_SEC_CSIGN_BI_04_C INC Not implemented
-TC_PRIV_CONN_BV_01_C INC Privacy feature - not implemented
-TC_PRIV_CONN_BV_02_C INC Privacy feature - not implemented
-TC_PRIV_CONN_BV_03_C INC Privacy feature - not implemented
-TC_PRIV_CONN_BV_04_C INC Privacy feature - not implemented
+TC_SEC_AUT_BV_20_C INC PTS issue #12284
+TC_SEC_AUT_BV_21_C PASS haltest: gattc register_client
+ gattc connect
+ bluetooth create_bond
+ gattc connect
+TC_SEC_AUT_BV_22_C PASS btmgmt io-cap 3
+ haltest: gattc register_client
+ gattc listen
+ gattc test_command 226 <addr> <u1> 1
+TC_SEC_AUT_BV_23_C PASS haltest: gatts register_server
+ gatts add_service 2 <uuid> 3
+ gatts add_characteristic 2 <service_handle>
+ <uuid> 10 34
+ gatts start_service 2 <service_handle> 1
+ gattc register_client
+ gattc listen
+ bluetooth ssp_reply
+ gatts send_response
+TC_SEC_AUT_BV_24_C PASS haltest: gatts register_server
+ gatts add_service 2 <uuid> 3
+ gatts add_characteristic 2 <service_handle>
+ <uuid> 10 34
+ gatts start_service 2 <service_handle> 1
+ gatts connect <PTS addr>
+ bluetooth ssp_reply
+ gatts disconnect
+ gatts connect
+ PTS asks for handle with insufficient encryption
+ gatts send_response
+TC_SEC_CSIGN_BV_01_C PASS haltest:
+ gattc connect
+ bluetooth create_bond
+ gattc connect
+ gattc write_characteristic: <write_type> 4
+ gattc disconnect
+TC_SEC_CSIGN_BV_02_C PASS haltest:
+ gatts add_service
+ gatts add_chaaracteristic:
+ <properties> 66
+ <permissions> 129
+ gatts start_service
+ gatts disconnect
+ gattc disconnect
+TC_SEC_CSIGN_BI_01_C PASS haltest:
+ gatts add_service
+ gatts add_chaaracteristic:
+ <properties> 66
+ <permissions> 129
+ gatts start_service
+ gatts disconnect
+ gattc disconnect
+TC_SEC_CSIGN_BI_02_C PASS haltest:
+ gatts add_service
+ gatts add_chaaracteristic:
+ <properties> 66
+ <permissions> 129
+ gatts start_service
+ gatts disconnect
+ gattc disconnect
+TC_SEC_CSIGN_BI_03_C PASS haltest:
+ gatts add_service
+ gatts add_characteristic:
+ <properties> 64
+ <permissions> 128
+ gatts start_service
+ gattc listen
+ bluetooth ssp_reply
+ gatts disconnect
+ bluetooth remove_bond
+TC_SEC_CSIGN_BI_04_C PASS haltest:
+ gatts add_service
+ gatts add_characteristic:
+ <properties> 64
+ <permissions> 256
+ gatts start_service
+ gattc listen
+ bluetooth ssp_reply
+ gatts disconnect
+TC_PRIV_CONN_BV_01_C PASS gattc connect
+ gattc create_bond
+ gattc search service
+ gattc get_characteristic
+ gattc write_characteristic (privacy flag)
+ gattc write_characteristic (reconnection addr.)
+ gattc disconnect
+TC_PRIV_CONN_BV_02_C PASS gattc connect
+ gattc search service
+ gattc get_characteristic
+ gattc write_characteristic (reconnection addr.)
+ gattc disconnect
+TC_PRIV_CONN_BV_03_C INC Privacy feature - PTS issue #12308
+TC_PRIV_CONN_BV_04_C PASS gattc connect
+ gattc create_bond
+ gattc search service
+ gattc get_characteristic
+ gattc write_characteristic (privacy flag)
+ gattc write_characteristic (reconnection addr.)
+ gattc disconnect
+ gattc connect
+ gattc disconnect
TC_PRIV_CONN_BV_05_C N/A
TC_PRIV_CONN_BV_06_C N/A
TC_PRIV_CONN_BV_07_C N/A
TC_PRIV_CONN_BV_08_C N/A
TC_PRIV_CONN_BV_09_C N/A
-TC_PRIV_CONN_BV_10_C N/A
-TC_PRIV_CONN_BV_11_C INC Privacy feature - not implemented
+TC_PRIV_CONN_BV_10_C INC PTS issue #12312
+TC_PRIV_CONN_BV_11_C INC PTS issue #12310
TC_ADV_BV_01_C N/A
-TC_ADV_BV_02_C N/A
-TC_ADV_BV_03_C N/A
+TC_ADV_BV_02_C FAIL PTS issue #12254
+TC_ADV_BV_03_C PASS gattc register_client
+ gattc listen 1 1
TC_ADV_BV_04_C N/A
-TC_ADV_BV_05_C N/A
+TC_ADV_BV_05_C PASS gattc register_client
+ gattc listen 1 1
TC_ADV_BV_06_C N/A
TC_ADV_BV_07_C N/A
TC_ADV_BV_08_C N/A
@@ -169,25 +365,34 @@
TC_GAT_BV_06_C N/A
TC_GAT_BV_07_C N/A
TC_GAT_BV_08_C N/A
-TC_DM_NCON_BV_01_C N/A
-TC_DM_CON_BV_01_C N/A
-TC_DM_NBON_BV_01_C N/A
+TC_DM_NCON_BV_01_C PASS
+TC_DM_CON_BV_01_C PASS
+TC_DM_NBON_BV_01_C PASS
TC_DM_BON_BV_01_C PASS haltest:
create_bond and remove_bond when requested
TC_DM_GIN_BV_01_C PASS
TC_DM_LIN_BV_01_C PASS
TC_DM_NAD_BV_01_C PASS Start discovery from IUT
TC_DM_NAD_BV_02_C PASS
-TC_DM_LEP_BV_01_C N/A
-TC_DM_LEP_BV_02_C N/A
-TC_DM_LEP_BV_03_C N/A
+TC_DM_LEP_BV_01_C PASS gattc register_client
+ gattc listen 1 1
+TC_DM_LEP_BV_02_C PASS Use basic rate PTS dongle
+ haltest:
+ bluetooth set_adapter_property
TC_DM_LEP_BV_04_C PASS l2test -n <PTS bdaddr>
TC_DM_LEP_BV_05_C PASS btmgmt find -b
l2test -n 00:1B:DC:06:06:22
TC_DM_LEP_BV_06_C PASS
-TC_DM_LEP_BV_07_C N/A
-TC_DM_LEP_BV_08_C N/A
-TC_DM_LEP_BV_09_C N/A
-TC_DM_LEP_BV_10_C N/A
-TC_DM_LEP_BV_11_C N/A
+TC_DM_LEP_BV_07_C PASS
+TC_DM_LEP_BV_08_C PASS
+TC_DM_LEP_BV_09_C PASS
+TC_DM_LEP_BV_10_C PASS haltest:
+ gattc listen 1
+ gattc listen 1 0
+ bluetooth start_discovery
+ bluetooth cancel_discovery
+ gattc listen 1
+ gattc listen 1 0
+ bluetooth create_bond
+TC_DM_LEP_BV_11_C PASS
-------------------------------------------------------------------------------
diff --git a/android/pts-gatt.txt b/android/pts-gatt.txt
index 342db47..9cf0758 100644
--- a/android/pts-gatt.txt
+++ b/android/pts-gatt.txt
@@ -1,7 +1,7 @@
-PTS test results for GAP
+PTS test results for GATT
PTS version: 5.1
-Tested: 7-May-2014
+Tested: 19-May-2014
Android version: 4.4.2
Results:
@@ -13,107 +13,1171 @@
-------------------------------------------------------------------------------
Test Name Result Notes
-------------------------------------------------------------------------------
-TC_GAC_CL_BV_01_C PASS Send exchanged MTU
-TC_GAC_SR_BV_01_C PASS IUT should accept incoming GATT con request
-TC_GAD_CL_BV_01_C PASS Send discover all primary services command to
- PTS
-TC_GAD_CL_BV_02_C PASS Send discover primary services (UUID=1800)
- command to PTS
-TC_GAD_CL_BV_03_C PASS Send discover all include service to PTS
-TC_GAD_CL_BV_04_C PASS Discover all characteristics of service (UUID=
- 180A)
-TC_GAD_CL_BV_05_C PASS Send discover characteristics by UUID
-TC_GAD_CL_BV_06_C PASS Send discover characteristics descriptor
-TC_GAD_CL_BV_07_C PASS
-TC_GAD_CL_BV_08_C PASS
-TC_GAD_SR_BV_01_C INC
-TC_GAD_SR_BV_02_C INC
-TC_GAD_SR_BV_03_C INC
-TC_GAD_SR_BV_04_C INC
-TC_GAD_SR_BV_05_C INC
-TC_GAD_SR_BV_06_C INC
-TC_GAD_SR_BV_07_C INC
-TC_GAD_SR_BV_08_C INC
-TC_GAR_CL_BV_01_C PASS
-TC_GAR_CL_BI_01_C PASS
-TC_GAR_CL_BI_02_C PASS
-TC_GAR_CL_BI_03_C PASS
-TC_GAR_CL_BI_04_C PASS
-TC_GAR_CL_BI_05_C PASS
-TC_GAR_CL_BV_03_C PASS
-TC_GAR_CL_BI_06_C PASS
-TC_GAR_CL_BI_07_C PASS
-TC_GAR_CL_BI_09_C PASS
-TC_GAR_CL_BI_10_C PASS
-TC_GAR_CL_BI_11_C PASS
-TC_GAR_CL_BV_04_C PASS
-TC_GAR_CL_BI_12_C PASS
-TC_GAR_CL_BI_13_C PASS
-TC_GAR_CL_BI_14_C PASS
-TC_GAR_CL_BI_15_C INC
-TC_GAR_CL_BI_16_C INC
-TC_GAR_CL_BI_17_C INC
-TC_GAR_CL_BV_05_C INC
-TC_GAR_CL_BI_18_C INC
-TC_GAR_CL_BI_19_C INC
-TC_GAR_CL_BI_20_C INC
-TC_GAR_CL_BI_21_C INC
-TC_GAR_CL_BI_22_C INC
-TC_GAR_CL_BV_06_C INC
-TC_GAR_CL_BI_23_C INC
-TC_GAR_CL_BI_24_C INC
-TC_GAR_CL_BI_25_C INC
-TC_GAR_CL_BI_26_C INC
-TC_GAR_CL_BI_27_C INC
-TC_GAR_CL_BV_07_C INC
-TC_GAR_CL_BI_28_C INC
-TC_GAR_CL_BI_29_C INC
-TC_GAR_CL_BI_30_C INC
-TC_GAR_CL_BI_31_C INC
-TC_GAR_CL_BI_32_C INC
-TC_GAR_CL_BI_33_C PASS
-TC_GAR_CL_BI_34_C INC
-TC_GAR_CL_BI_35_C PASS
-TC_GAR_SR_BV_01_C INC
-TC_GAR_SR_BI_01_C INC
-TC_GAR_SR_BI_02_C INC
-TC_GAR_SR_BI_03_C INC
-TC_GAR_SR_BI_04_C INC
-TC_GAR_SR_BI_05_C INC
-TC_GAR_SR_BV_03_C INC
-TC_GAR_SR_BI_06_C INC
-TC_GAR_SR_BI_07_C INC
-TC_GAR_SR_BI_08_C INC
-TC_GAR_SR_BI_09_C INC
-TC_GAR_SR_BI_10_C INC
-TC_GAR_SR_BI_11_C INC
-TC_GAR_SR_BV_04_C INC
-TC_GAR_SR_BI_12_C INC
-TC_GAR_SR_BI_13_C INC
-TC_GAR_SR_BI_14_C INC
-TC_GAR_SR_BI_15_C INC
-TC_GAR_SR_BI_16_C INC
-TC_GAR_SR_BI_17_C INC
-TC_GAR_SR_BV_05_C INC
-TC_GAR_SR_BI_18_C INC
-TC_GAR_SR_BI_19_C INC
-TC_GAR_SR_BI_20_C INC
-TC_GAR_SR_BI_21_C INC
-TC_GAR_SR_BI_22_C INC
-TC_GAR_SR_BV_06_C INC
-TC_GAR_SR_BI_23_C INC
-TC_GAR_SR_BI_24_C INC
-TC_GAR_SR_BI_25_C INC
-TC_GAR_SR_BI_26_C INC
-TC_GAR_SR_BI_27_C INC
-TC_GAR_SR_BV_07_C INC
-TC_GAR_SR_BV_08_C INC
-TC_GAR_SR_BI_28_C INC
-TC_GAR_SR_BI_29_C INC
-TC_GAR_SR_BI_30_C INC
-TC_GAR_SR_BI_31_C INC
-TC_GAR_SR_BI_32_C INC
-TC_GAR_SR_BI_33_C INC
-TC_GAR_SR_BI_34_C INC
-TC_GAR_SR_BI_35_C INC
+TC_GAC_CL_BV_01_C PASS haltest:
+ gattc scan
+ gattc search_service
+ gattc get_characteristic
+ gattc write_characteristic: type 3
+TC_GAC_SR_BV_01_C PASS haltest:
+ gatts add_service
+ gatts add_chaaracteristic:
+ <properties> 10 <permissions> 17
+ gatts start_service
+ gatts send_response:
+ <data> value greater than MTU
+ repeat with correct offset
+ gatts send_response:
+ <data> value greater than MTU
+ repeat with correct offset
+TC_GAD_CL_BV_01_C PASS haltest:
+ gattc register_client
+ gattc scan
+ gattc connect
+ gattc search_service
+ gattc disconnect
+ gattc connect
+ gattc refresh - NOTE: refresh should be called
+ otherwise services are being read from the cache
+TC_GAD_CL_BV_02_C PASS haltest:
+ gattc register_client
+ gattc scan
+ gattc connect
+ gattc search_service with given uuid
+ gattc disconnect
+ gattc connect
+ gattc refresh
+TC_GAD_CL_BV_03_C PASS haltest:
+ when requested: gattc get_characteristic
+TC_GAD_CL_BV_04_C PASS haltest:
+ when requested: gattc get_characteristic
+TC_GAD_CL_BV_05_C PASS haltest:
+ when requested: gattc get_characteristic
+ handle: check from btmon logs
+TC_GAD_CL_BV_06_C PASS haltest:
+ when requested: gattc get_descriptor
+TC_GAD_CL_BV_07_C N/A
+TC_GAD_CL_BV_08_C N/A
+TC_GAD_SR_BV_01_C PASS haltest:
+ gattc register_client
+ gattc listen
+ gatts register_server
+ gatts add_service
+ gatts add_characteristic
+ gatts start_service
+ gatts add_service
+ gatts add_included_service
+ gatts start_service
+TC_GAD_SR_BV_02_C PASS haltest:
+ gattc register_client
+ gattc listen
+ gatts register_server
+ gatts add_service
+ gatts add_characteristic
+ gatts start_service
+ gatts add_service
+ gatts add_included_service
+ gatts start_service
+TC_GAD_SR_BV_03_C PASS haltest:
+ gattc register_client
+ gattc listen
+ gatts register_server
+ gatts add_service
+ gatts add_characteristic
+ gatts start_service
+ gatts add_service
+ gatts add_included_service
+ gatts start_service
+TC_GAD_SR_BV_04_C PASS haltest:
+ gattc register_client
+ gattc listen
+ gatts register_server
+ gatts add_service
+ gatts add_characteristic
+ gatts start_service
+ gatts add_service
+ gatts add_included_service
+ gatts start_service
+TC_GAD_SR_BV_05_C PASS haltest:
+ gattc register_client
+ gattc listen
+ gatts register_server
+ gatts add_service
+ gatts add_characteristic
+ gatts start_service
+ gatts add_service
+ gatts add_included_service
+ gatts start_service
+TC_GAD_SR_BV_06_C PASS haltest:
+ gattc register_client
+ gattc listen
+ gatts register_server
+ gatts add_service
+ gatts add_characteristic
+ gatts start_service
+ gatts add_service
+ gatts add_included_service
+ gatts start_service
+TC_GAD_SR_BV_07_C N/A
+TC_GAD_SR_BV_08_C N/A
+TC_GAR_CL_BV_01_C PASS haltest:
+ gattc read_characteristic
+TC_GAR_CL_BI_01_C PASS haltest:
+ gattc read_characteristic
+TC_GAR_CL_BI_02_C PASS haltest:
+ gattc read_characteristic
+TC_GAR_CL_BI_03_C PASS haltest:
+ gattc read_characteristic
+TC_GAR_CL_BI_04_C PASS haltest:
+ gattc read_characteristic
+TC_GAR_CL_BI_05_C PASS haltest:
+ gattc connect
+ gattc search_service
+ gattc get_characteristic: srvc_id based on
+ handle from logs
+ gattc read_characteristic
+ gattc disconnect
+TC_GAR_CL_BV_03_C PASS haltest:
+ gattc connect
+ test_command: <cmd> 224 [u1] 8
+ test_command: <cmd> 224 [u1] 8
+ gattc disconnect
+TC_GAR_CL_BI_06_C PASS haltest:
+ gattc connect
+ test_command: <cmd> 224 [u1] 8
+ gattc disconnect
+TC_GAR_CL_BI_07_C PASS haltest:
+ gattc connect
+ test_command: <cmd> 224 [u1] 8
+ gattc disconnect
+TC_GAR_CL_BI_09_C PASS haltest:
+ gattc connect
+ test_command: <cmd> 224 [u1] 8
+ gattc disconnect
+TC_GAR_CL_BI_10_C PASS haltest:
+ gattc connect
+ test_command: <cmd> 224 [u1] 8
+ gattc disconnect
+TC_GAR_CL_BI_11_C PASS haltest:
+ gattc connect
+ test_command: <cmd> 224 [u1] 8
+ gattc disconnect
+TC_GAR_CL_BV_04_C PASS haltest:
+ gattc read_characteristic
+TC_GAR_CL_BI_12_C PASS haltest:
+ gattc read_characteristic
+TC_GAR_CL_BI_13_C PASS haltest:
+ gattc read_characteristic
+TC_GAR_CL_BI_14_C PASS haltest:
+ gattc read_characteristic
+TC_GAR_CL_BI_15_C PASS haltest:
+ gattc read_characteristic
+TC_GAR_CL_BI_16_C PASS haltest:
+ gattc read_characteristic
+TC_GAR_CL_BI_17_C PASS haltest:
+ gattc connect
+ gattc search_service
+ gattc get_characteristic: srvc_id based on
+ handle from logs
+ gattc read_characteristic
+ gattc disconnect
+TC_GAR_CL_BV_05_C N/A
+TC_GAR_CL_BI_18_C N/A
+TC_GAR_CL_BI_19_C N/A
+TC_GAR_CL_BI_20_C N/A
+TC_GAR_CL_BI_21_C N/A
+TC_GAR_CL_BI_22_C N/A
+TC_GAR_CL_BV_06_C PASS haltest:
+ gattc read_descriptor
+TC_GAR_CL_BI_23_C PASS haltest:
+ gattc read_descriptor
+TC_GAR_CL_BI_24_C PASS haltest:
+ gattc read_descriptor
+TC_GAR_CL_BI_25_C PASS haltest:
+ gattc read_descriptor
+TC_GAR_CL_BI_26_C PASS haltest:
+ gattc read_descriptor
+TC_GAR_CL_BI_27_C PASS haltest:
+ gattc connect
+ gattc search_service
+ gattc get_characteristic: srvc_id based on
+ handle from logs
+ gattc get_descriptor: srvc_id based on
+ handle from logs
+ gattc read_descriptor
+ gattc disconnect
+TC_GAR_CL_BV_07_C PASS haltest:
+ gattc read_descriptor
+TC_GAR_CL_BI_28_C PASS haltest:
+ gattc read_descriptor
+TC_GAR_CL_BI_29_C PASS haltest:
+ gattc read_descriptor
+TC_GAR_CL_BI_30_C PASS haltest:
+ gattc read_descriptor
+TC_GAR_CL_BI_31_C PASS haltest:
+ gattc read_descriptor
+TC_GAR_CL_BI_32_C PASS haltest:
+ gattc read_descriptor
+TC_GAR_CL_BI_33_C PASS haltest:
+ gattc connect
+ gattc search_service
+ gattc get_characteristic: srvc_id based on
+ handle from logs
+ gattc get_descriptor: srvc_id based on
+ handle from logs
+ gattc read_descriptor
+ gattc disconnect
+TC_GAR_CL_BI_34_C N/A
+TC_GAR_CL_BI_35_C PASS haltest:
+ gattc read_characteristic
+TC_GAR_SR_BV_01_C PASS
+TC_GAR_SR_BI_01_C PASS
+TC_GAR_SR_BI_02_C PASS
+TC_GAR_SR_BI_03_C PASS haltest:
+ gatts add_service
+ gatts add_characteristic:
+ <properties> 2 <permissions> 1
+ gatts start_service
+ gatts send_response: <status> 8
+TC_GAR_SR_BI_04_C PASS haltest:
+ gatts add_service
+ gatts add_chaaracteristic:
+ <properties> 2 <permissions> 3
+ gatts start_service
+ gatts send_response
+TC_GAR_SR_BI_05_C PASS haltest:
+ gatts add_service
+ gatts add_characteristic:
+ <properties> 2 <permissions> 1
+ gatts start_service
+ gatts send_response: <status> 12
+TC_GAR_SR_BV_03_C PASS
+TC_GAR_SR_BI_06_C PASS haltest:
+ gatts add_service
+ gatts add_chaaracteristic:
+ <properties> 2 <permissions> 16
+ gatts start_service
+TC_GAR_SR_BI_07_C PASS
+TC_GAR_SR_BI_08_C PASS
+TC_GAR_SR_BI_09_C PASS haltest:
+ gatts add_service
+ gatts add_chaaracteristic:
+ <properties> 2 <permissions> 1
+ gatts start_service
+ gatts send_response: <status> 8
+TC_GAR_SR_BI_10_C PASS haltest:
+ gatts add_service
+ gatts add_chaaracteristic:
+ <properties> 2 <permissions> 1
+ gatts start_service
+ gatts send_response: <status> 5
+TC_GAR_SR_BI_11_C PASS haltest:
+ gatts add_service
+ gatts add_chaaracteristic:
+ <properties> 2 <permissions> 1
+ gatts start_service
+ gatts send_response: <status> 12
+TC_GAR_SR_BV_04_C PASS haltest:
+ gatts add_service
+ gatts add_chaaracteristic:
+ <properties> 2 <permissions> 1
+ gatts start_service
+ gatts send_response:
+ <data> value greater than MTU
+ repeat with correct offset
+TC_GAR_SR_BI_12_C PASS haltest:
+ gatts add_service
+ gatts add_chaaracteristic:
+ <properties> 8 <permissions> 16
+ gatts start_service
+ gatts send_response
+TC_GAR_SR_BI_13_C PASS haltest:
+ gatts add_service
+ gatts add_chaaracteristic:
+ <properties> 2 <permissions> 1
+ gatts start_service
+ gatts send_response:
+ <data> value greater than MTU
+ repeat with correct offset
+ gatts send_response: <status> 7
+TC_GAR_SR_BI_14_C PASS haltest:
+ gatts add_service
+ gatts add_characteristic:
+ <properties> 2 <permissions> 1
+ gatts start_service
+ gatts send_response: <status> 1
+TC_GAR_SR_BI_15_C PASS haltest:
+ gatts add_service
+ gatts add_characteristic:
+ <properties> 2 <permissions> 1
+ gatts start_service
+ gatts send_response: <status> 8
+TC_GAR_SR_BI_16_C PASS haltest:
+ gatts add_service
+ gatts add_characteristic:
+ <properties> 2 <permissions> 1
+ gatts start_service
+ gatts send_response: <status> 5
+TC_GAR_SR_BI_17_C PASS haltest:
+ gatts add_service
+ gatts add_characteristic:
+ <properties> 2 <permissions> 1
+ gatts start_service
+ gatts send_response: <status> 12
+TC_GAR_SR_BV_05_C N/A
+TC_GAR_SR_BI_18_C N/A
+TC_GAR_SR_BI_19_C N/A
+TC_GAR_SR_BI_20_C N/A
+TC_GAR_SR_BI_21_C N/A
+TC_GAR_SR_BI_22_C N/A
+TC_GAR_SR_BV_06_C PASS haltest:
+ gatts add_service
+ gatts add_characteristic:
+ <properties> 2 <permissions> 1
+ gatts add_descriptor
+ gatts start_service
+ gatts send_response
+TC_GAR_SR_BI_23_C PASS haltest:
+ gatts add_service
+ gatts add_chaaracteristic:
+ <properties> 2 <permissions> 1
+ gatts add_descriptor: <permissions> 16
+ gatts start_service
+TC_GAR_SR_BI_24_C PASS haltest:
+ gatts add_service
+ gatts add_characteristic:
+ <properties> 2 <permissions> 1
+ gatts add_descriptor
+ gatts start_service
+ gatts send_response: <status> 1
+TC_GAR_SR_BI_25_C PASS haltest:
+ gatts add_service
+ gatts add_chaaracteristic:
+ <properties> 2 <permissions> 1
+ gatts add_descriptor: <permissions> 1
+ gatts start_service
+ gatts send_response: <status> 8
+TC_GAR_SR_BI_26_C PASS haltest:
+ gatts add_service
+ gatts add_chaaracteristic:
+ <properties> 2 <permissions> 1
+ gatts add_descriptor: <permissions> 1
+ gatts start_service
+ gatts send_response: <status> 5
+TC_GAR_SR_BI_27_C PASS haltest:
+ gatts add_service
+ gatts add_chaaracteristic:
+ <properties> 2 <permissions> 1
+ gatts add_descriptor: <permissions> 1
+ gatts start_service
+ gatts send_response: <status> 12
+TC_GAR_SR_BV_07_C PASS haltest:
+ gatts add_service
+ gatts add_chaaracteristic:
+ <properties> 2 <permissions> 1
+ gatts add_descriptor: <permissions> 1
+ gatts start_service
+ gatts send_response:
+ <data> value greater than MTU
+ repeat with correct offset
+TC_GAR_SR_BV_08_C PASS haltest:
+ gatts add_service
+ gatts add_chaaracteristic:
+ <properties> 2 <permissions> 1
+ gatts add_descriptor: <permissions> 1
+ gatts start_service
+ gatts send_response:
+ <data> value greater than MTU
+ repeat with correct offset
+TC_GAR_SR_BI_28_C PASS haltest:
+ gatts add_service
+ gatts add_chaaracteristic:
+ <properties> 2 <permissions> 1
+ gatts add_descriptor: <permissions> 16
+ gatts start_service
+TC_GAR_SR_BI_29_C PASS haltest:
+ gatts add_service
+ gatts add_chaaracteristic:
+ <properties> 2 <permissions> 1
+ gatts add_descriptor: <permissions> 1
+ gatts start_service
+ gatts send_response:
+ <data> value greater than MTU
+ repeat with correct offset
+ gatts send_response: <status> 7
+TC_GAR_SR_BI_30_C PASS haltest:
+ gatts add_service
+ gatts add_chaaracteristic:
+ <properties> 2 <permissions> 1
+ gatts add_descriptor: <permissions> 1
+ gatts start_service
+ gatts send_response: <status> 1
+TC_GAR_SR_BI_31_C PASS haltest:
+ gatts add_service
+ gatts add_chaaracteristic:
+ <properties> 2 <permissions> 1
+ gatts add_descriptor: <permissions> 1
+ gatts start_service
+ gatts send_response: <status> 8
+TC_GAR_SR_BI_32_C PASS haltest:
+ gatts add_service
+ gatts add_chaaracteristic:
+ <properties> 2 <permissions> 1
+ gatts add_descriptor: <permissions> 1
+ gatts start_service
+ gatts send_response: <status> 5
+TC_GAR_SR_BI_33_C PASS haltest:
+ gatts add_service
+ gatts add_chaaracteristic:
+ <properties> 2 <permissions> 1
+ gatts add_descriptor: <permissions> 1
+ gatts start_service
+ gatts send_response: <status> 12
+TC_GAR_SR_BI_34_C N/A
+TC_GAR_SR_BI_35_C N/A
+TC_GAW_CL_BV_01_C PASS haltest:
+ gattc connect
+ gattc search_service
+ gattc get_characteristic: srvc_id based on
+ handle from logs
+ gattc write_characteristic
+ gattc disconnect
+TC_GAW_CL_BV_02_C PASS haltest:
+ bluetooth create_bond
+ gattc disconnect
+ gattc connect
+ gattc search_service
+ gattc get_characteristics
+ gattc write_characteristics: <type> 4
+ gattc disconnect
+
+TC_GAW_CL_BV_03_C PASS haltest:
+ gattc connect
+ gattc search_service
+ gattc get_characteristic: srvc_id based on
+ handle from logs
+ gattc write_characteristic
+ gattc disconnect
+TC_GAW_CL_BI_02_C PASS haltest:
+ gattc connect
+ test_command: <cmd> 225 [u1] 18
+ gattc disconnect
+TC_GAW_CL_BI_03_C PASS haltest:
+ gattc connect
+ gattc search_service
+ gattc get_characteristic: srvc_id based on
+ handle from logs
+ gattc write_characteristic 2 <long_value>
+ gattc disconnect
+TC_GAW_CL_BI_04_C PASS haltest:
+ gattc connect
+ gattc search_service
+ gattc get_characteristic: srvc_id based on
+ handle from logs
+ gattc write_characteristic 2 <long value>
+ gattc disconnect
+TC_GAW_CL_BI_05_C PASS haltest:
+ gattc connect
+ gattc search_service
+ gattc get_characteristic: srvc_id based on
+ handle from logs
+ gattc write_characteristic 2 <long_value>
+ gattc disconnect
+TC_GAW_CL_BI_06_C PASS haltest:
+ gattc connect
+ gattc search_service
+ gattc get_characteristic: srvc_id based on
+ handle from logs
+ gattc write_characteristic 2 <long_value>
+ gattc disconnect
+TC_GAW_CL_BV_05_C PASS haltest:
+ gattc connect
+ gattc search_service
+ gattc get_characteristic: srvc_id based on
+ handle from logs
+ gattc write_characteristic 2 <long_value>
+ gattc disconnect
+TC_GAW_CL_BI_07_C PASS haltest:
+ gattc connect
+ test_command: <cmd> 225 [u1] 22
+ gattc disconnect
+TC_GAW_CL_BI_08_C PASS haltest:
+ gattc connect
+ gattc search_service
+ gattc get_characteristic: srvc_id based on
+ handle from logs
+ gattc write_characteristic 2 <long_value>
+ gattc disconnect
+TC_GAW_CL_BI_09_C PASS haltest:
+ gattc connect
+ gattc search_service
+ gattc get_characteristic: srvc_id based on
+ handle from logs
+ gattc write_characteristic 2 <long_value>
+ gattc disconnect
+TC_GAW_CL_BI_11_C PASS haltest:
+ gattc connect
+ gattc search_service
+ gattc get_characteristic: srvc_id based on
+ handle from logs
+ gattc write_characteristic 2 <long_value>
+ gattc disconnect
+TC_GAW_CL_BI_12_C PASS haltest:
+ gattc connect
+ gattc search_service
+ gattc get_characteristic: srvc_id based on
+ handle from logs
+ gattc write_characteristic 2 <long_value>
+ gattc disconnect
+TC_GAW_CL_BI_13_C PASS haltest:
+ gattc connect
+ gattc search_service
+ gattc get_characteristic: srvc_id based on
+ handle from logs
+ gattc write_characteristic 2 <long_value>
+ gattc disconnect
+TC_GAW_CL_BV_06_C PASS haltest:
+ gattc connect
+ gattc search_service
+ gattc get_characteristic: srvc_id based on
+ handle from logs
+ gattc write_characteristic 2 <long_value>
+ gattc disconnect
+TC_GAW_CL_BI_14_C PASS haltest:
+ gattc connect
+ test_command: <cmd> 225 [u1] 22
+ gattc disconnect
+TC_GAW_CL_BI_15_C PASS haltest:
+ gattc connect
+ gattc search_service
+ gattc get_characteristic: srvc_id based on
+ handle from logs
+ gattc write_characteristic 2 <long_value>
+ gattc disconnect
+TC_GAW_CL_BI_17_C PASS haltest:
+ gattc connect
+ gattc search_service
+ gattc get_characteristic: srvc_id based on
+ handle from logs
+ gattc write_characteristic 2 <long_value>
+ gattc disconnect
+TC_GAW_CL_BI_18_C PASS haltest:
+ gattc connect
+ gattc search_service
+ gattc get_characteristic: srvc_id based on
+ handle from logs
+ gattc write_characteristic 2 <long_value>
+ gattc disconnect
+TC_GAW_CL_BI_19_C PASS haltest:
+ gattc connect
+ gattc search_service
+ gattc get_characteristic: srvc_id based on
+ handle from logs
+ gattc write_characteristic 2 <long_value>
+ gattc disconnect
+TC_GAW_CL_BV_08_C PASS haltest:
+ gattc connect
+ gattc search_service
+ gattc get_characteristic: srvc_id based on
+ handle from logs
+ gattc get_descriptor
+ gattc write_descriptor 2 <short_value>
+ gattc disconnect
+TC_GAW_CL_BI_20_C PASS haltest:
+ gattc connect
+ test_command: <cmd> 225 [u1] 18
+ gattc disconnect
+TC_GAW_CL_BI_21_C PASS haltest:
+ gattc connect
+ gattc search_service
+ gattc get_characteristic: srvc_id based on
+ handle from logs
+ gattc get_descriptor
+ gattc write_descriptor 2 <short_value>
+ gattc disconnect
+TC_GAW_CL_BI_22_C PASS haltest:
+ gattc connect
+ gattc search_service
+ gattc get_characteristic: srvc_id based on
+ handle from logs
+ gattc get_descriptor
+ gattc write_descriptor 2 <short_value>
+ gattc disconnect
+TC_GAW_CL_BI_23_C PASS haltest:
+ gattc connect
+ gattc search_service
+ gattc get_characteristic: srvc_id based on
+ handle from logs
+ gattc get_descriptor
+ gattc write_descriptor 2 <short_value>
+ gattc disconnect
+TC_GAW_CL_BI_24_C PASS haltest:
+ gattc connect
+ gattc search_service
+ gattc get_characteristic: srvc_id based on
+ handle from logs
+ gattc get_descriptor
+ gattc write_descriptor 2 <short_value>
+ gattc disconnect
+TC_GAW_CL_BV_09_C PASS haltest:
+ gattc connect
+ gattc search_service
+ gattc get_characteristic: srvc_id based on
+ handle from logs
+ gattc get_descriptor
+ gattc write_descriptor 2 <long_value>
+ gattc disconnect
+TC_GAW_CL_BI_25_C PASS haltest:
+ gattc connect
+ test_command: <cmd> 225 [u1] 22
+ gattc disconnect
+TC_GAW_CL_BI_26_C PASS haltest:
+ gattc connect
+ gattc search_service
+ gattc get_characteristic: srvc_id based on
+ handle from logs
+ gattc write_characteristic 2 <long_value>
+ gattc disconnect
+TC_GAW_CL_BI_27_C PASS haltest:
+ gattc connect
+ gattc search_service
+ gattc get_characteristic: srvc_id based on
+ handle from logs
+ gattc write_characteristic 2 <long_value>
+ gattc disconnect
+TC_GAW_CL_BI_29_C PASS haltest:
+ gattc connect
+ gattc search_service
+ gattc get_characteristic: srvc_id based on
+ handle from logs
+ gattc write_characteristic 2 <long_value>
+ gattc disconnect
+TC_GAW_CL_BI_30_C PASS haltest:
+ gattc connect
+ gattc search_service
+ gattc get_characteristic: srvc_id based on
+ handle from logs
+ gattc write_characteristic 2 <long_value>
+ gattc disconnect
+TC_GAW_CL_BI_31_C PASS haltest:
+ gattc connect
+ gattc search_service
+ gattc get_characteristic: srvc_id based on
+ handle from logs
+ gattc write_characteristic 2 <long_value>
+ gattc disconnect
+TC_GAW_CL_BI_32_C PASS haltest:
+ gattc connect
+ gattc search_service
+ gattc get_characteristic: srvc_id based on
+ handle from logs
+ gattc write_characteristic 2 <long_value>
+ gattc execute_write
+ gattc disconnect
+TC_GAW_CL_BI_33_C PASS haltest:
+ gattc connect
+ gattc search_service
+ gattc get_characteristic: srvc_id based on
+ handle from logs
+ gattc write_characteristic 2
+ gattc disconnect
+TC_GAW_CL_BI_34_C PASS haltest:
+ gattc connect
+ gattc search_service
+ gattc get_characteristic: srvc_id based on
+ handle from logs
+ gattc write_characteristic 2 <long_value>
+ gattc disconnect
+TC_GAW_CL_BI_35_C PASS haltest:
+ gattc connect
+ gattc search_service
+ gattc get_characteristic: srvc_id based on
+ handle from logs
+ gattc get_descriptor
+ gattc write_descriptor 2
+ gattc disconnect
+TC_GAW_CL_BI_36_C PASS haltest:
+ gattc connect
+ gattc search_service
+ gattc get_characteristic: srvc_id based on
+ handle from logs
+ gattc get_descriptor
+ gattc write_descriptor 2 <long_value>
+ gattc disconnect
+TC_GAW_SR_BV_01_C PASS haltest:
+ gatts add_service
+ gatts add_characteristic:
+ <properties> 4 <permissions> 16
+ gatts start_service
+TC_GAW_SR_BV_02_C PASS haltest:
+ gatts add service
+ gatts add_characteristics:
+ <properties> 66 <permisions> 147
+ gatts start_service
+ gattc listen
+ gatts send_response: (twice)
+ NOTE: gatts_request_write_cb shall be called
+ (verify it)
+TC_GAW_SR_BI_01_C PASS haltest:
+ gatts add_service
+ gatts add_characteristic:
+ <properties> 68
+ <permissions> 129
+ gatts start_service
+ gatts send_response: repeat with <data> 1
+TC_GAW_SR_BV_03_C PASS haltest:
+ gatts add_service
+ gatts add_characteristic:
+ <properties> 10 <permissions> 17
+TC_GAW_SR_BI_02_C PASS haltest:
+ gatts add_service
+ gatts add_characteristic:
+ <properties> 10 <permissions> 17
+ gatts start_service
+ gatts send_response: <status> 1
+TC_GAW_SR_BI_03_C PASS haltest:
+ gatts add_service
+ gatts add_characteristic:
+ <properties> 10 <permissions> 1
+ gatts start_service
+TC_GAW_SR_BI_04_C PASS haltest:
+ gatts add_service
+ gatts add_characteristic:
+ <properties> 10 <permissions> 17
+ gatts start_service
+ gatts send_response: <status> 8
+TC_GAW_SR_BI_05_C PASS haltest:
+ gatts add_service
+ gatts add_characteristic:
+ <properties> 10 <permissions> 17
+ gatts start_service
+ gatts send_response: <status> 5
+TC_GAW_SR_BI_06_C PASS haltest:
+ gatts add_service
+ gatts add_characteristic:
+ <properties> 10 <permissions> 17
+ gatts start_service
+ gatts send_response: <status> 12
+TC_GAW_SR_BV_05_C PASS haltest:
+ gatts add_service
+ gatts add_characteristic:
+ <properties> 10 <permissions> 17
+ gatts start_service
+ gatts send_response:
+ <data> value greater than MTU
+ repeat with correct offset
+ gatts send_response:
+ repeat with correct value
+TC_GAW_SR_BI_07_C PASS haltest:
+ gatts add_service
+ gatts add_characteristic:
+ <properties> 10 <permissions> 17
+ gatts start_service
+ gatts send_response
+TC_GAW_SR_BI_08_C PASS haltest:
+ gatts add_service
+ gatts add_characteristic:
+ <properties> 2 <permissions> 1
+ gatts start_service
+TC_GAW_SR_BI_09_C PASS haltest:
+ gatts add_service
+ gatts add_characteristic:
+ <properties> 10 <permissions> 17
+ gatts start_service
+ gatts send_response:
+ <data> value greater than MTU
+ repeat with correct offset
+ gatts send_response: <status> 7
+TC_GAW_SR_BI_11_C PASS haltest:
+ gatts add_service
+ gatts add_characteristic:
+ <properties> 10 <permissions> 17
+ gatts start_service
+ gatts send_response:
+ <data> value greater than MTU
+ repeat with correct offset
+ gatts send_response: <status> 8
+TC_GAW_SR_BI_12_C PASS haltest:
+ gatts add_service
+ gatts add_characteristic:
+ <properties> 10 <permissions> 17
+ gatts start_service
+ gatts send_response:
+ <data> value greater than MTU
+ repeat with correct offset
+ gatts send_response: <status> 5
+TC_GAW_SR_BI_13_C PASS haltest:
+ gatts add_service
+ gatts add_characteristic:
+ <properties> 10 <permissions> 17
+ gatts start_service
+ gatts send_response:
+ <data> value greater than MTU
+ repeat with correct offset
+ gatts send_response: <status> 12
+TC_GAW_SR_BV_06_C PASS haltest:
+ gatts add_service
+ gatts add_characteristic:
+ <properties> 10 <permissions> 17
+ gatts start_service
+ gatts send_response:
+ repeat with correct value
+TC_GAW_SR_BV_10_C PASS haltest:
+ gatts add_service
+ gatts add_characteristic:
+ <properties> 10 <permissions> 17
+ gatts start_service
+ gatts send_response:
+ <data> value greater than MTU
+ repeat with correct offset
+ gatts send_response:
+ repeat with correct value
+TC_GAW_SR_BI_14_C PASS haltest:
+ gatts add_service
+ gatts add_characteristic:
+ <properties> 10 <permissions> 17
+ gatts start_service
+ gatts send_response: <status> 1
+TC_GAW_SR_BI_15_C PASS haltest:
+ gatts add_service
+ gatts add_characteristic:
+ <properties> 10 <permissions> 17
+ gatts start_service
+ gatts send_response: <status> 3
+TC_GAW_SR_BI_17_C PASS haltest:
+ gatts add_service
+ gatts add_characteristic:
+ <properties> 10 <permissions> 17
+ gatts start_service
+ gatts send_response: <status> 8
+TC_GAW_SR_BI_18_C PASS haltest:
+ gatts add_service
+ gatts add_characteristic:
+ <properties> 10 <permissions> 17
+ gatts start_service
+ gatts send_response: <status> 5
+TC_GAW_SR_BI_19_C PASS haltest:
+ gatts add_service
+ gatts add_characteristic:
+ <properties> 10 <permissions> 17
+ gatts start_service
+ gatts send_response: <status> 12
+TC_GAW_SR_BV_07_C PASS haltest:
+ gatts add_service
+ gatts add_characteristic:
+ <properties> 10 <permissions> 17
+ gatts start_service
+ gatts send_response:
+ repeat with correct value
+TC_GAW_CL_BV_08_C PASS haltest:
+ gatts add_service
+ gatts add_characteristic:
+ <properties> 10 <permissions> 17
+ gatts add_descriptor: <permmisions> 17
+ gatts start_service
+ gatts send_response
+TC_GAW_SR_BI_20_C PASS haltest:
+ gatts add_service
+ gatts add_characteristic:
+ <properties> 10 <permissions> 17
+ gatts add_descriptor: <permmisions> 17
+ gatts start_service
+ gatts send_response: <status> 1
+TC_GAW_SR_BI_21_C PASS haltest:
+ gatts add_service
+ gatts add_characteristic:
+ <properties> 2 <permissions> 1
+ gatts add_descriptor: <permmisions> 1
+ gatts start_service
+TC_GAW_SR_BI_22_C PASS haltest:
+ gatts add_service
+ gatts add_characteristic:
+ <properties> 10 <permissions> 17
+ gatts add_descriptor: <permmisions> 17
+ gatts start_service
+ gatts send_response: <status> 8
+
+TC_GAW_SR_BI_23_C PASS haltest:
+ gatts add_service
+ gatts add_characteristic:
+ <properties> 10 <permissions> 17
+ gatts add_descriptor: <permmisions> 17
+ gatts start_service
+ gatts send_response: <status> 5
+TC_GAW_SR_BI_24_C PASS haltest:
+ gatts add_service
+ gatts add_characteristic:
+ <properties> 10 <permissions> 17
+ gatts add_descriptor: <permmisions> 17
+ gatts start_service
+ gatts send_response: <status> 12
+TC_GAW_SR_BV_09_C PASS haltest:
+ gatts add_service
+ gatts add_characteristic:
+ <properties> 10 <permissions> 17
+ gatts add_characteristic:
+ <properties> 10 <permissions> 17
+ gatts start_service
+ gatts send_response:
+ <data> value greater than MTU
+ repeat with correct offset
+ gatts send_response:
+ repeat with correct value
+TC_GAW_SR_BI_25_C PASS haltest:
+ gatts add_service
+ gatts add_characteristic:
+ <properties> 10 <permissions> 17
+ gatts add_descriptor: <permmisions> 17
+ gatts start_service
+ gatts send_response: <status> 1
+TC_GAW_SR_BI_26_C PASS haltest:
+ gatts add_service
+ gatts add_characteristic:
+ <properties> 10 <permissions> 17
+ gatts add_descriptor: <permmisions> 1
+ gatts start_service
+TC_GAW_SR_BI_27_C PASS haltest:
+ gatts add_service
+ gatts add_characteristic:
+ <properties> 10 <permissions> 17
+ gatts add_descriptor: <permmisions> 1
+ gatts start_service
+ gatts send_response:
+ <data> value greater than MTU
+ repeat with correct offset
+ gatts send_response: <status> 7
+TC_GAW_SR_BI_29_C PASS haltest:
+ gatts add_service
+ gatts add_characteristic:
+ <properties> 10 <permissions> 17
+ gatts add_descriptor: <permmisions> 17
+ gatts start_service
+ gatts send_response: <status> 8
+TC_GAW_SR_BI_30_C PASS haltest:
+ gatts add_service
+ gatts add_characteristic:
+ <properties> 10 <permissions> 17
+ gatts add_descriptor: <permmisions> 17
+ gatts start_service
+ gatts send_response: <status> 5
+TC_GAW_SR_BI_31_C PASS haltest:
+ gatts add_service
+ gatts add_characteristic:
+ <properties> 10 <permissions> 17
+ gatts add_descriptor: <permmisions> 17
+ gatts start_service
+ gatts send_response: <status> 12
+TC_GAW_SR_BI_32_C PASS haltest:
+ gatts add_service
+ gatts add_characteristic:
+ <properties> 10 <permissions> 17
+ gatts start_service
+ gatts send_response
+ gatts send_response: <status> 13
+TC_GAW_SR_BI_33_C PASS haltest:
+ gatts add_service
+ gatts add_characteristic:
+ <properties> 10 <permissions> 17
+ gatts start_service
+ gatts send_response:
+ <data> value greater than MTU
+ repeat with correct offset
+ gatts send_response: <status> 13
+TC_GAW_SR_BI_34_C PASS haltest:
+ gatts add_service
+ gatts add_characteristic:
+ <properties> 10 <permissions> 17
+ gatts add_descriptor: <permmisions> 17
+ gatts start_service
+ gatts send_response
+ gatts send_response: <status> 13
+TC_GAW_SR_BI_35_C PASS haltest:
+ gatts add_service
+ gatts add_characteristic:
+ <properties> 10 <permissions> 17
+ gatts add_descriptor: <permmisions> 17
+ gatts start_service
+ gatts send_response:
+ <data> value greater than MTU
+ repeat with correct offset
+ gatts send_response: <status> 13
+TC_GAN_CL_BV_01_C PASS haltest:
+ gattc connect
+ gattc search_service
+ gattc get_characteristic: srvc_id based on
+ handle from logs
+ gattc get_descriptor
+ gattc write_descriptor 2 <hex_value> 0001
+ gattc disconnect
+TC_GAN_SR_BV_01_C PASS haltest:
+ gatts add_service
+ gatts add_chaaracteristic:
+ <properties> 26 <permissions> 17
+ gatts add_descriptor: <uuid> 2902
+ gatts start_service
+ gatts send_response
+ gatts send_response
+ gatts send_indication:
+ <attr_handle> char value handle
+ <confirm> 0
+TC_GAI_CL_BV_01_C PASS haltest:
+ gattc connect
+ gattc search_service
+ gattc get_characteristic: srvc_id based on
+ handle from logs
+ gattc get_descriptor
+ gattc write_descriptor 2 <hex_value> 0002
+ gattc disconnect
+TC_GAI_SR_BV_01_C PASS haltest:
+ gatts add_service
+ gatts add_chaaracteristic:
+ <properties> 42 <permissions> 17
+ gatts add_descriptor: <permissions> 17
+ gatts start_service
+ gatts add_service
+ gatts start_service
+TC_GAS_CL_BV_01_C PASS haltest:
+ gattc connect
+ gattc disconnect
+TC_GAS_SR_BV_01_C PASS haltest:
+ gatts add_service
+ gatts add_chaaracteristic:
+ <properties> 42 <permissions> 17
+ gatts add_descriptor: <permissions> 17
+ gatts start_service
+ gatts add_service
+ gatts start_service
+TC_GAT_CL_BV_01_C PASS haltest:
+ gattc connect
+ gattc search_service
+ gattc get_characteristic: srvc_id based on
+ handle from logs
+ gattc read_characcteristic
+ gattc disconnect
+TC_GAT_CL_BV_02_C PASS haltest:
+ gattc connect
+ gattc search_service
+ gattc get_characteristic: srvc_id based on
+ handle from logs
+ gattc write_characcteristic 2 <short_value>
+ gattc disconnect
+TC_GAT_SR_BV_01_C PASS haltest:
+ gatts add_service
+ gatts add_chaaracteristic:
+ <properties> 42 <permissions> 17
+ gatts add_descriptor: <permissions> 17
+ gatts start_service
+ gatts add_service
+ gatts start_service
+TC_GPA_CL_BV_01_C PASS haltest:
+ gattc connect
+ test_command: <cmd> 224 [u1] 8
+ gattc disconnect
+TC_GPA_CL_BV_02_C PASS haltest:
+ gattc connect
+ test_command: <cmd> 224 [u1] 8
+ gattc disconnect
+TC_GPA_CL_BV_03_C PASS haltest:
+ gattc connect
+ test_command: <cmd> 224 [u1] 8
+ gattc disconnect
+TC_GPA_CL_BV_04_C PASS haltest:
+ gattc connect
+ test_command: <cmd> 224 [u1] 8
+ gattc disconnect
+TC_GPA_CL_BV_05_C PASS haltest:
+ gattc connect
+ test_command: <cmd> 224 [u1] 8
+ gattc disconnect
+TC_GPA_CL_BV_06_C PASS haltest:
+ gattc connect
+ test_command: <cmd> 224 [u1] 8
+ gattc disconnect
+TC_GPA_CL_BV_07_C PASS haltest:
+ gattc connect
+ test_command: <cmd> 224 [u1] 8
+ gattc disconnect
+TC_GPA_CL_BV_08_C PASS haltest:
+ gattc connect
+ test_command: <cmd> 224 [u1] 8
+ gattc disconnect
+TC_GPA_CL_BV_11_C PASS haltest:
+ gattc connect
+ Repeat following steps 5 times:
+ 1.Find Characteristic Aggregate Format
+ gattc test_command <cmd> 224 [u1] 8
+ 2.Read aggregate descriptor
+ gattc test_command <cmd> 224 [u1] 10
+ 3.Read 3 handles from aggregate descriptor
+ value
+ gattc test_command <cmd> 224 [u1] 10
+ 4.Compare descriptors values
+ gattc disconnect
+
+TC_GPA_CL_BV_12_C PASS haltest:
+ gattc connect
+ Repeat following steps 5 times:
+ 1.Find Characteristic Presentation Format
+ gattc test_command <cmd> 224 [u1] 8
+ 2.Find characteristic in this range
+ gattc test_command <cmd> 224 <uuid> 2803 [u1] 8
+ 3.Read characteristic declaration
+ gattc test_command <cmd> 224 [u1] 10
+ 4.Read characteristic value
+ gattc test_command <cmd> 224 [u1] 10
+ 5.Compare characteristic value and
+ presentation format
+ gattc disconnect
+TC_GPA_SR_BV_01_C PASS
+TC_GPA_SR_BV_02_C PASS haltest:
+ gatts add_service
+ gatts start_service
+TC_GPA_SR_BV_03_C PASS haltest:
+ gatts add_service
+ gatts add_service
+ add_included_service
+ gatts start_service
+ gatts start_service
+TC_GPA_SR_BV_04_C PASS haltest:
+ gatts add_service
+ gatts add_chaaracteristic:
+ <properties> 10 <permissions> 17
+ gatts start_service
+TC_GPA_SR_BV_05_C PASS haltest:
+ gatts add_service
+ gatts add_chaaracteristic:
+ <properties> 138 <permissions> 17
+ gatts add_descriptor <UUID> 2900
+ gatts start_service
+ gatts send_response
+TC_GPA_SR_BV_06_C PASS haltest:
+ gatts add_service
+ gatts add_chaaracteristic:
+ <properties> 138 <permissions> 17
+ gatts add_descriptor <UUID> 2901
+ gatts start_service
+ gatts send_response
+TC_GPA_SR_BV_07_C PASS
+TC_GPA_SR_BV_08_C PASS haltest:
+ gatts add_service
+ gatts add_chaaracteristic:
+ <properties> 138 <permissions> 17
+ gatts add_descriptor <UUID> 2903
+ gatts start_service
+ gatts send_response
+TC_GPA_SR_BV_11_C PASS haltest:
+ gatts add_service
+ gatts add_chaaracteristic:
+ <properties> 138 <permissions> 17
+ gatts add_descriptor <UUID> 2905
+ gatts start_service
+ gatts send_response: repeat with correct offset
+ and data
+TC_GPA_SR_BV_12_C PASS PTS issue #12262
+ haltest:
+ gatts add_service
+ gatts add_chaaracteristic:
+ <properties> 10 <permissions> 17
+ gatts add_descriptor <UUID> 2904
+ gatts start_service
+ gatts send_response: repeat with correct data
-------------------------------------------------------------------------------
diff --git a/android/pts-hdp.txt b/android/pts-hdp.txt
index 1dec18c..bdf8eca 100644
--- a/android/pts-hdp.txt
+++ b/android/pts-hdp.txt
@@ -1,7 +1,7 @@
PTS test results for HDP
PTS version: 5.1
-Tested: not tested
+Tested: 17-June-2014
Android version: 4.4.2
Results:
@@ -13,18 +13,51 @@
-------------------------------------------------------------------------------
Test Name Result Notes
-------------------------------------------------------------------------------
-TC_SRC_CON_BV_01_I INC
-TC_SRC_CON_BV_02_I INC
-TC_SRC_CON_BV_03_I INC
-TC_SRC_CON_BV_04_I INC
-TC_SRC_CON_BV_05_I INC
-TC_SRC_CON_BV_06_I INC
-TC_SRC_CON_BV_07_I INC
-TC_SRC_CON_BV_08_I INC
-TC_SRC_CON_BV_09_I INC
-TC_SRC_CON_BV_10_I INC
-TC_SRC_CC_BV_01_C INC
-TC_SRC_CC_BV_02_C INC
+TC_SRC_CON_BV_01_I PASS haltest:
+ bluetooth enable
+
+ bluetooth set_adapter_property
+ BT_PROPERTY_ADAPTER_SCAN_MODE
+ BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE
+
+ hl register_application <args>
+ for instance:
+ hl register_application health intel heartrate
+ heartrate-monitor 1 BTHL_MDEP_ROLE_SOURCE 4100
+ BTHL_CHANNEL_TYPE_RELIABLE testing
+
+ when prompted:
+ bluetooth ssp_reply <args>
+ for instance:
+ bluetooth ssp_reply <bdaddr>
+ BT_SSP_VARIANT_CONSENT 1
+TC_SRC_CON_BV_02_I PASS
+TC_SRC_CON_BV_03_I PASS when prompted: bluetooth ssp_reply <args>
+TC_SRC_CON_BV_04_I PASS haltest:
+ hl connect_channel <app_id> <bd_addr>
+ <mdep_cfg_index>
+
+ when prompted: bluetooth ssp_reply <args>
+TC_SRC_CON_BV_05_I PASS when prompted: bluetooth ssp_reply <args>
+TC_SRC_CON_BV_06_I PASS haltest:
+ hl connect_channel <app_id> <bd_addr>
+ <mdep_cfg_index>
+
+ when prompted: bluetooth ssp_reply <args>
+TC_SRC_CON_BV_07_I PASS when prompted: bluetooth start_discovery
+TC_SRC_CON_BV_08_I PASS when prompted: bluetooth ssp_reply <args>
+TC_SRC_CON_BV_09_I PASS haltest:
+ hl connect_channel <app_id> <bd_addr>
+ <mdep_cfg_index>
+
+ when prompted: bluetooth ssp_reply <args>
+TC_SRC_CON_BV_10_I N/A
+TC_SRC_CC_BV_01_C PASS haltest:
+ hl connect_channel <app_id> <bd_addr>
+ <mdep_cfg_index>
+
+ when prompted: bluetooth ssp_reply <args>
+TC_SRC_CC_BV_02_C PASS when prompted: bluetooth ssp_reply <args>
TC_SRC_CC_BV_03_C INC
TC_SRC_CC_BV_05_C INC
TC_SRC_CC_BV_07_C INC
@@ -41,18 +74,45 @@
TC_SRC_DE_BV_02_I INC
TC_SRC_DEP_BV_01_I INC
TC_SRC_DEP_BV_02_I N/A
-TC_SNK_CON_BV_01_I INC
-TC_SNK_CON_BV_02_I INC
-TC_SNK_CON_BV_03_I INC
-TC_SNK_CON_BV_04_I INC
-TC_SNK_CON_BV_05_I INC
-TC_SNK_CON_BV_06_I INC
-TC_SNK_CON_BV_07_I INC
-TC_SNK_CON_BV_08_I INC
-TC_SNK_CON_BV_09_I INC
-TC_SNK_CON_BV_10_I INC
-TC_SNK_CC_BV_01_C INC
-TC_SNK_CC_BV_02_C INC
+TC_SNK_CON_BV_01_I PASS haltest:
+ hl register_application <args>
+ for instance:
+ hl register_application health intel heartrate
+ heartrate-monitor 1 BTHL_MDEP_ROLE_SINK 4100
+ BTHL_CHANNEL_TYPE_RELIABLE testing
+
+ when prompted:
+ bluetooth ssp_reply <args>
+ for instance:
+ bluetooth ssp_reply <bdaddr>
+ BT_SSP_VARIANT_CONSENT 1
+TC_SNK_CON_BV_02_I PASS
+TC_SNK_CON_BV_03_I PASS when prompted: bluetooth ssp_reply <args>
+TC_SNK_CON_BV_04_I PASS haltest:
+ hl connect_channel <app_id> <bd_addr>
+ <mdep_cfg_index>
+
+ when prompted: bluetooth ssp_reply <args>
+TC_SNK_CON_BV_05_I PASS when prompted: bluetooth ssp_reply <args>
+TC_SNK_CON_BV_06_I PASS haltest:
+ hl connect_channel <app_id> <bd_addr>
+ <mdep_cfg_index>
+
+ when prompted: bluetooth ssp_reply <args>
+TC_SNK_CON_BV_07_I PASS when prompted: bluetooth start_discovery
+TC_SNK_CON_BV_08_I PASS when prompted: bluetooth ssp_reply <args>
+TC_SNK_CON_BV_09_I PASS haltest:
+ hl connect_channel <app_id> <bd_addr>
+ <mdep_cfg_index>
+
+ when prompted: bluetooth ssp_reply <args>
+TC_SNK_CON_BV_10_I N/A
+TC_SNK_CC_BV_01_C PASS haltest:
+ hl connect_channel <app_id> <bd_addr>
+ <mdep_cfg_index>
+
+ when prompted: bluetooth ssp_reply <args>
+TC_SNK_CC_BV_02_C PASS when prompted: bluetooth ssp_reply <args>
TC_SNK_CC_BV_04_C INC
TC_SNK_CC_BV_06_C INC
TC_SNK_CC_BV_08_C INC
diff --git a/android/pts-hogp.txt b/android/pts-hogp.txt
new file mode 100644
index 0000000..9b962b4
--- /dev/null
+++ b/android/pts-hogp.txt
@@ -0,0 +1,80 @@
+PTS test results for HoG
+
+PTS version: 5.1
+Tested: 17-June-2014
+Android version: 4.4.2
+
+Results:
+PASS test passed
+FAIL test failed
+INC test is inconclusive
+N/A test is disabled due to PICS setup
+
+-------------------------------------------------------------------------------
+Test Name Result Notes
+-------------------------------------------------------------------------------
+TC_HGDS_HH_BV_01_I PASS
+TC_HGDS_HH_BV_02_I PASS
+TC_HGDS_HH_BV_03_I PASS
+TC_HGDS_HD_BV_01_I N/A
+TC_HGDS_HD_BV_02_I N/A
+TC_HGDR_RH_BV_01_I PASS
+TC_HGDC_RH_BV_01_I PASS
+TC_HGDC_RH_BV_02_I PASS
+TC_HGDC_RH_BV_03_I PASS
+TC_HGDC_RH_BV_04_I PASS
+TC_HGDC_RH_BV_05_I PASS
+TC_HGDC_RH_BV_06_I PASS
+TC_HGDC_RH_BV_07_I PASS
+TC_HGDC_HH_BV_08_I PASS
+TC_HGDC_HH_BV_14_I PASS
+TC_HGDC_HH_BV_15_I PASS
+TC_HGDC_HH_BV_16_I PASS
+TC_HGDC_BH_BV_09_I N/A
+TC_HGDC_BH_BV_10_I N/A
+TC_HGDC_BH_BV_11_I N/A
+TC_HGDC_BH_BV_12_I N/A
+TC_HGDC_BH_BV_13_I N/A
+TC_HGRF_RH_BV_01_I INC
+TC_HGRF_RH_BV_02_I INC
+TC_HGRF_RH_BV_03_I INC
+TC_HGRF_RH_BV_04_I INC
+TC_HGRF_RH_BV_05_I INC
+TC_HGRF_RH_BV_19_I INC
+TC_HGRF_RH_BV_06_I INC
+TC_HGRF_RH_BV_07_I INC
+TC_HGRF_RH_BV_08_I INC
+TC_HGRF_RH_BV_09_I INC
+TC_HGRF_HH_BV_10_I INC
+TC_HGRF_HH_BV_11_I INC
+TC_HGRF_HH_BV_12_I INC
+TC_HGRF_BH_BV_13_I N/A
+TC_HGRF_BH_BV_14_I N/A
+TC_HGRF_BH_BV_15_I N/A
+TC_HGRF_BH_BV_16_I N/A
+TC_HGRF_BH_BV_17_I N/A
+TC_HGRF_HH_BV_18_I INC
+TC_HGWF_RH_BV_01_I INC
+TC_HGWF_RH_BV_02_I INC
+TC_HGWF_RH_BV_03_I INC
+TC_HGWF_RH_BV_04_I INC
+TC_HGWF_RH_BV_05_I INC
+TC_HGWF_RH_BV_06_I INC
+TC_HGWF_RH_BV_07_I INC
+TC_HGWF_BH_BV_08_I N/A
+TC_HGWF_BH_BV_09_I N/A
+TC_HGWF_BH_BV_10_I N/A
+TC_HGWF_BH_BV_11_I N/A
+TC_HGCF_RH_BV_01_I INC
+TC_HGCF_RH_BV_02_I INC
+TC_HGCF_BH_BV_03_I N/A
+TC_HGCF_BH_BV_04_I N/A
+TC_HGCF_BH_BV_05_I N/A
+TC_HGCF_BH_BV_06_I N/A
+TC_HGNF_RH_BV_01_I INC
+TC_HGNF_RH_BI_01_I INC
+TC_HGNF_RH_BI_01_I INC
+TC_HGNF_BH_BV_02_I N/A
+TC_HGNF_BH_BV_03_I N/A
+TC_HGNF_BH_BI_01_I N/A
+-------------------------------------------------------------------------------
diff --git a/android/pts-l2cap.txt b/android/pts-l2cap.txt
index b14614a..6e012c1 100644
--- a/android/pts-l2cap.txt
+++ b/android/pts-l2cap.txt
@@ -41,15 +41,15 @@
TC_COS_ECH_BV_01_C PASS
TC_COS_ECH_BV_02_C PASS l2ping <bdaddr>
TC_CLS_CLR_BV_01_C N/A
-TC_CLS_UCD_BV_01_C N/A
-TC_CLS_UCD_BV_02_C N/A
-TC_CLS_UCD_BV_03_C N/A
+TC_CLS_UCD_BV_01_C INC not tested yet
+TC_CLS_UCD_BV_02_C INC not tested yet
+TC_CLS_UCD_BV_03_C INC not tested yet
TC_EXF_BV_01_C PASS
TC_EXF_BV_02_C PASS
TC_EXF_BV_03_C PASS
-TC_EXF_BV_04_C N/A
+TC_EXF_BV_04_C INC not tested yet
TC_EXF_BV_05_C PASS
-TC_EXF_BV_06_C N/A
+TC_EXF_BV_06_C INC not tested yet
TC_CMC_BV_01_C PASS l2test -X ertm -P <PSM - not reserved>
TC_CMC_BV_02_C PASS l2test -X ertm -P <PSM - not reserved>
@@ -109,7 +109,7 @@
TC_ERM_BV_14_C PASS
TC_ERM_BV_15_C PASS l2test -X ertm -P <PSM - not reserved> -N 4
-Y 4
-TC_ERM_BV_16_C N/A
+TC_ERM_BV_16_C INC not tested yet
TC_ERM_BV_17_C PASS l2test -X ertm -P <PSM - not reserved>
TC_ERM_BV_18_C PASS
TC_ERM_BV_19_C PASS l2test -x -X ertm -P <PSM - not reserved> -N 1
@@ -117,7 +117,7 @@
TC_ERM_BV_21_C PASS l2test -w -X ertm -P 4097 -N 3
TC_ERM_BV_22_C PASS
TC_ERM_BV_23_C PASS
-TC_ERM_BI_01_C N/A
+TC_ERM_BI_01_C INC not tested yet
TC_ERM_BI_02_C PASS l2test -X ertm -P <PSM - not reserved>
TC_ERM_BI_03_C PASS
TC_ERM_BI_04_C PASS
@@ -129,11 +129,11 @@
TC_STM_BV_11_C N/A
TC_STM_BV_12_C N/A
TC_STM_BV_13_C N/A
-TC_FIX_BV_01_C N/A
+TC_FIX_BV_01_C INC not tested yet
TC_FIX_BV_02_C N/A
-TC_EWC_BV_01_C N/A
-TC_EWC_BV_02_C N/A
-TC_EWC_BV_03_C N/A
+TC_EWC_BV_01_C INC not tested yet
+TC_EWC_BV_02_C INC not tested yet
+TC_EWC_BV_03_C INC not tested yet
TC_LSC_BV_01_C N/A
TC_LSC_BV_02_C N/A
TC_LSC_BV_03_C N/A
@@ -150,16 +150,16 @@
TC_CCH_BV_02_C N/A
TC_CCH_BV_03_C N/A
TC_CCH_BV_04_C N/A
-TC_ECF_BV_01_C N/A
-TC_ECF_BV_02_C N/A
-TC_ECF_BV_03_C N/A
-TC_ECF_BV_04_C N/A
-TC_ECF_BV_05_C N/A
-TC_ECF_BV_06_C N/A
-TC_ECF_BV_07_C N/A
-TC_ECF_BV_08_C N/A
-TC_LE_CPU_BV_01_C N/A
-TC_LE_CPU_BV_02_C N/A
-TC_LE_CPU_BI_01_C N/A
-TC_LE_CPU_BI_02_C N/A
-TC_LE_REJ_BV_01_C N/A
+TC_ECF_BV_01_C INC not tested yet
+TC_ECF_BV_02_C INC not tested yet
+TC_ECF_BV_03_C INC not tested yet
+TC_ECF_BV_04_C INC not tested yet
+TC_ECF_BV_05_C INC not tested yet
+TC_ECF_BV_06_C INC not tested yet
+TC_ECF_BV_07_C INC not tested yet
+TC_ECF_BV_08_C INC not tested yet
+TC_LE_CPU_BV_01_C INC not tested yet
+TC_LE_CPU_BV_02_C INC not tested yet
+TC_LE_CPU_BI_01_C INC not tested yet
+TC_LE_CPU_BI_02_C INC not tested yet
+TC_LE_REJ_BV_01_C INC not tested yet
diff --git a/android/pts-mps.txt b/android/pts-mps.txt
index 005318d..93bce31 100644
--- a/android/pts-mps.txt
+++ b/android/pts-mps.txt
@@ -1,7 +1,7 @@
PTS test results for MPS
PTS version: 5.1
-Tested: not tested
+Tested: 23.05.2014
Android version: 4.4.2
Results:
@@ -14,17 +14,17 @@
-------------------------------------------------------------------------------
Test Name Result Notes
-------------------------------------------------------------------------------
-TC_AG_SRC_HFAV_ACT_SD_BV_01_I INC
-TC_AG_SRC_HFAV_ACT_SD_BV_02_I INC
-TC_AG_SRC_HFAV_ACT_SD_BV_03_I INC
-TC_AG_SRC_HFAV_CLH_SD_BV_01_I INC
+TC_AG_SRC_HFAV_ACT_SD_BV_01_I PASS
+TC_AG_SRC_HFAV_ACT_SD_BV_02_I PASS
+TC_AG_SRC_HFAV_ACT_SD_BV_03_I PASS
+TC_AG_SRC_HFAV_CLH_SD_BV_01_I PASS
TC_AG_SRC_HFAV_CLH_SD_BV_02_I N/A
-TC_AG_SRC_HFAV_CLH_SD_BV_03_I INC
-TC_AG_SRC_HFAV_CLH_SD_BV_04_I INC
-TC_AG_SRC_HFAV_CLH_SD_BV_05_I INC
-TC_AG_SRC_HFAV_CLH_SD_BV_06_I INC
-TC_AVP_CTH_SD_BI_01_I INC
-TC_AVP_CTH_SD_BI_02_I INC
+TC_AG_SRC_HFAV_CLH_SD_BV_03_I PASS
+TC_AG_SRC_HFAV_CLH_SD_BV_04_I PASS
+TC_AG_SRC_HFAV_CLH_SD_BV_05_I PASS
+TC_AG_SRC_HFAV_CLH_SD_BV_06_I PASS
+TC_AVP_CTH_SD_BI_01_I PASS
+TC_AVP_CTH_SD_BI_02_I PASS
TC_HF_SNK_HFAV_ACT_SD_BV_01_I N/A
TC_HF_SNK_HFAV_ACT_SD_BV_02_I N/A
TC_HF_SNK_HFAV_ACT_SD_BV_03_I N/A
@@ -41,13 +41,13 @@
TC_HF_SNK_CT_HFAV_CLH_MD_BV_04_I N/A
TC_HF_SNK_CT_HFAV_CLH_MD_BV_05_I N/A
TC_HF_SNK_CT_HFAV_CLH_MD_BV_06_I N/A
-TC_SDP_CTH_SD_BV_01_I INC
-TC_SRC_TG_HFAV_ACT_MD_BV_01_I INC
-TC_SRC_TG_HFAV_CLH_MD_BV_01_I INC
-TC_SRC_TG_HFAV_CLH_MD_BV_02_I INC
-TC_SRC_TG_HFAV_CLH_MD_BV_03_I INC
-TC_SRC_TG_HFAV_CLH_MD_BV_04_I INC
-TC_SRC_TG_HFAV_CLH_MD_BV_05_I INC
-TC_SRC_TG_HFAV_CLH_MD_BV_06_I INC
-TC_PAIRING_HF_SNK_CT INC
+TC_SDP_CTH_SD_BV_01_I FAIL PTS issue #12245
+TC_SRC_TG_HFAV_ACT_MD_BV_01_I PASS
+TC_SRC_TG_HFAV_CLH_MD_BV_01_I PASS
+TC_SRC_TG_HFAV_CLH_MD_BV_02_I PASS
+TC_SRC_TG_HFAV_CLH_MD_BV_03_I PASS
+TC_SRC_TG_HFAV_CLH_MD_BV_04_I PASS
+TC_SRC_TG_HFAV_CLH_MD_BV_05_I PASS
+TC_SRC_TG_HFAV_CLH_MD_BV_06_I PASS
+TC_PAIRING_HF_SNK_CT PASS
-------------------------------------------------------------------------------
diff --git a/android/pts-opp.txt b/android/pts-opp.txt
index 0a070ca..e1b0def 100644
--- a/android/pts-opp.txt
+++ b/android/pts-opp.txt
@@ -28,9 +28,9 @@
TC_CLIENT_BCP_BV_04_I N/A
TC_CLIENT_BCP_BV_05_I N/A
TC_CLIENT_CON_BV_01_C N/A
-TC_CLIENT_OPH_BI_01_C PASS PTS issue: ID: 12081 (bluetooth.org)
+TC_CLIENT_OPH_BI_01_C PASS PTS issue #12081
ETS-12081 required
-TC_CLIENT_OPH_BV_01_I PASS PTS issue: ID: 12081.
+TC_CLIENT_OPH_BV_01_I PASS PTS issue #12081
ETS-12160 required
TC_CLIENT_OPH_BV_02_I N/A
TC_CLIENT_OPH_BV_03_I PASS ETS-12081 required (bluetooth.org)
@@ -48,10 +48,13 @@
TC_CLIENT_OPH_BV_16_I N/A
TC_CLIENT_OPH_BV_17_I N/A
TC_CLIENT_OPH_BV_18_I N/A
-TC_CLIENT_OPH_BV_19_I PASS ETS-12081 required
+TC_CLIENT_OPH_BV_19_I PASS PTS issue #12081
+ ETS-12081 required
TC_CLIENT_OPH_BV_20_I PASS
-TC_CLIENT_OPH_BV_22_I PASS ETS-12081 required
-TC_CLIENT_OPH_BV_23_I PASS ETS-12081 required
+TC_CLIENT_OPH_BV_22_I PASS PTS issue #12081
+ ETS-12081 required
+TC_CLIENT_OPH_BV_23_I PASS PTS issue #12081
+ ETS-12081 required
TC_CLIENT_OPH_BV_24_I N/A
TC_CLIENT_OPH_BV_25_I N/A
TC_CLIENT_OPH_BV_26_I N/A
@@ -73,43 +76,53 @@
TC_SERVER_BCE_BV_06_I N/A
TC_SERVER_BCE_BV_07_I N/A
TC_SERVER_BCP_BV_01_I N/A
-TC_SERVER_BCP_BV_02_I PASS ETS-12081 required (bluetooth.org)
+TC_SERVER_BCP_BV_02_I PASS PTS issue #12081
+ ETS-12081 required
TC_SERVER_BCP_BV_03_I N/A
TC_SERVER_BCP_BV_04_I N/A
TC_SERVER_BCP_BV_05_I N/A
TC_SERVER_CON_BV_02_C N/A
TC_SERVER_OPH_BV_01_I PASS
TC_SERVER_OPH_BV_02_I PASS
-TC_SERVER_OPH_BV_03_I PASS ETS-12081 required
+TC_SERVER_OPH_BV_03_I PASS PTS issue #12081
+ ETS-12081 required
IUT must be in the connectable mode. Tester must
accept incoming file
-TC_SERVER_OPH_BV_04_I PASS ETS-12081 required
+TC_SERVER_OPH_BV_04_I PASS PTS issue #12081
+ ETS-12081 required
IUT must be in the connectable mode. Tester must
accept incoming file
-TC_SERVER_OPH_BV_05_I PASS ETS-12081 required
+TC_SERVER_OPH_BV_05_I PASS PTS issue #12081
+ ETS-12081 required
IUT must be in the connectable mode. Tester must
reject incoming file
TC_SERVER_OPH_BV_07_I N/A
TC_SERVER_OPH_BV_08_I N/A
TC_SERVER_OPH_BV_09_I N/A
-TC_SERVER_OPH_BV_10_I PASS ETS-12081 required
+TC_SERVER_OPH_BV_10_I PASS PTS issue #12081
+ ETS-12081 required
IUT must be in the connectable mode
TC_SERVER_OPH_BV_11_I N/A
TC_SERVER_OPH_BV_12_I N/A
TC_SERVER_OPH_BV_13_I N/A
-TC_SERVER_OPH_BV_14_I PASS ETS-12081 required
+TC_SERVER_OPH_BV_14_I PASS PTS issue #12081
+ ETS-12081 required
IUT must be in the connectable mode
TC_SERVER_OPH_BV_15_I N/A
TC_SERVER_OPH_BV_16_I N/A
TC_SERVER_OPH_BV_17_I N/A
-TC_SERVER_OPH_BV_18_I PASS ETS-12081 required
+TC_SERVER_OPH_BV_18_I PASS PTS issue #12081
+ ETS-12081 required
IUT must be in the connectable mode
-TC_SERVER_OPH_BV_19_I PASS ETS-12081 required
+TC_SERVER_OPH_BV_19_I PASS PTS issue #12081
+ ETS-12081 required
IUT must be in the connectable mode
TC_SERVER_OPH_BV_21_I N/A
-TC_SERVER_OPH_BV_22_I PASS ETS-12081 required
+TC_SERVER_OPH_BV_22_I PASS PTS issue #12081
+ ETS-12081 required
IUT must be in the connectable mode
-TC_SERVER_OPH_BV_23_I PASS ETS-12081 required
+TC_SERVER_OPH_BV_23_I PASS PTS issue #12081
+ ETS-12081 required
IUT must be in the connectable mode
TC_SERVER_OPH_BV_24_I N/A
TC_SERVER_OPH_BV_25_I N/A
@@ -129,5 +142,6 @@
TC_SERVER_OPH_BV_31_I N/A
TC_SERVER_OPH_BV_32_I N/A
TC_SERVER_OPH_BV_33_I N/A
-TC_SERVER_OPH_BV_34_I PASS ETS-12081 required
+TC_SERVER_OPH_BV_34_I PASS PTS issue #12081
+ ETS-12081 required
-------------------------------------------------------------------------------
diff --git a/android/pts-sm.txt b/android/pts-sm.txt
index 0792de8..ff713cc 100644
--- a/android/pts-sm.txt
+++ b/android/pts-sm.txt
@@ -3,6 +3,7 @@
PTS version: 5.1
Tested: 14-May-2014
Android version: 4.4.2
+kernel version: 3.17
Results:
PASS test passed
@@ -15,49 +16,85 @@
Test Name Result Notes
-------------------------------------------------------------------------------
TC_PROT_BV_01_C PASS
-TC_PROT_BV_02_C FAIL JIRA: BZ-24
+TC_PROT_BV_02_C PASS
btmgmt advertising on
-TC_JW_BV_01_C FAIL JIRA: BZ-25
-TC_JW_BV_02_C INC JIRA: BZ-26
-TC_JW_BV_05_C INC
+TC_JW_BV_01_C PASS btmgmt pairable off
+ btmgmt pair -c 0x03 -t 0x01 <addr>
+TC_JW_BV_02_C PASS
+TC_JW_BV_05_C PASS btmgmt pair -c 0x03 -t 0x01 <addr>
TC_JW_BI_01_C PASS
-TC_JW_BI_02_C INC
-TC_JW_BI_03_C INC
-TC_JW_BI_04_C FAIL
-TC_PKE_BV_01_C FAIL
-TC_PKE_BV_02_C INC
-TC_PKE_BV_04_C FAIL
-TC_PKE_BV_05_C INC
-TC_PKE_BI_01_C INC
+TC_JW_BI_02_C PASS
+TC_JW_BI_03_C PASS bluetoothd is NOT running
+ btmgmt power on
+ btmgmt le on
+ btmgmt connectable on
+ btmgmt pairable on
+ btmgmt discov on
+ btmgmt advertising on
+TC_JW_BI_04_C PASS btmgmt pairable off
+ btmgmg pair -c 0x03 -t 0x01 <addr>
+TC_PKE_BV_01_C PASS btmgmt pairable off
+ btmgmt pair -c 0x04 -t 0x01 <addr>
+ Note: provide passkey to PTS
+TC_PKE_BV_02_C PASS btmgmt pairable off
+ btmgmt io-cap 0x04
+ btmgmt advertising on
+ btmgmt monitor
+ Note: provide passkey
+TC_PKE_BV_04_C PASS btmgmt pair -c 0x04 -t 0x01 <addr>
+TC_PKE_BV_05_C PASS btmgmt io-cap 0x04
+ l2test -r -J4 -AES -V le_public
+TC_PKE_BI_01_C PASS btmgmt pair -c 0x04 -t 0x01 <addr>
+ Note: Enter invalid passkey in PTS
TC_PKE_BI_02_C PASS
-TC_PKE_BI_03_C INC
-TC_OOB_BV_01_C FAIL
-TC_OOB_BV_02_C INC
-TC_OOB_BV_03_C FAIL
-TC_OOB_BV_04_C INC
-TC_OOB_BV_05_C INC
-TC_OOB_BV_06_C INC
-TC_OOB_BV_07_C INC
-TC_OOB_BV_08_C INC
-TC_OOB_BV_09_C INC
-TC_OOB_BV_10_C INC
-TC_OOB_BI_01_C INC
-TC_OOB_BI_02_C INC
-TC_EKS_BV_01_C INC
-TC_EKS_BV_02_C INC
-TC_EKS_BI_01_C INC
-TC_EKS_BI_02_C INC
-TC_SIGN_BV_01_C INC
-TC_SIGN_BV_03_C INC
-TC_SIGN_BI_01_C INC
-TC_KDU_BV_01_C INC
-TC_KDU_BV_02_C INC
-TC_KDU_BV_03_C INC
-TC_KDU_BV_04_C INC
-TC_KDU_BV_05_C INC
-TC_KDU_BV_06_C INC
-TC_KDU_BV_07_C INC
-TC_SIP_BV_01_C INC
-TC_SIP_BV_02_C FAIL
-TC_SIE_BV_01_C INC
+TC_PKE_BI_03_C PASS btmgmt io-cap 0x04
+ btmgmt advertising on
+ btmgmt monitor
+ Note: Enter invalid passkey in PTS
+TC_OOB_BV_01_C NA
+TC_OOB_BV_02_C NA
+TC_OOB_BV_03_C NA
+TC_OOB_BV_04_C NA
+TC_OOB_BV_05_C PASS
+TC_OOB_BV_06_C PASS
+TC_OOB_BV_07_C PASS
+TC_OOB_BV_08_C PASS
+TC_OOB_BV_09_C NA
+TC_OOB_BV_10_C NA
+TC_OOB_BI_01_C NA
+TC_OOB_BI_02_C NA
+TC_EKS_BV_01_C PASS
+TC_EKS_BV_02_C PASS
+TC_EKS_BI_01_C PASS btmgmt io-cap 0x03
+TC_EKS_BI_02_C PASS
+TC_SIGN_BV_01_C INC PTS issue #12305
+TC_SIGN_BV_03_C PASS haltest
+ gattc listen
+
+TC_SIGN_BI_01_C PASS haltest
+ gattc listen
+TC_KDU_BV_01_C PASS btmgmt pairable on
+TC_KDU_BV_02_C PASS PTS issue #12302
+ Note: Can pass it with following instructions:
+ btmgmt privacy on
+ btmgmt advetising on
+ Check our random address (valid for 15 min)
+ Set PIXIT TSPX_bd_addr_iut to random address
+ Set PIXIT TSPX_peer_type to 01
+TC_KDU_BV_03_C PASS btmgmt pairable on
+ btmgmt advertising on
+
+TC_KDU_BV_04_C INC PTS issue #12301
+TC_KDU_BV_05_C PASS PTS issue #12302
+ Note: Can pass it with following instructions:
+ btmgmt privacy on
+ Check our random address (valid for 15 min)
+ Set PIXIT TSPX_bd_addr_iut to random address
+ Set PIXIT TSPX_peer_type to 01
+TC_KDU_BV_06_C FAIL PTS issue #12301
+ PTS issue #12302
+TC_KDU_BV_07_C PASS btmgmt pairable on
+TC_SIP_BV_01_C PASS btmgmt pair -c 0x03 -t 0x01 <addr>
+TC_SIP_BV_02_C PASS l2test -n -J4 -V le_public <addr>
+TC_SIE_BV_01_C INC PTS issue #12306
-------------------------------------------------------------------------------
diff --git a/android/socket.c b/android/socket.c
index 99d6bec..2b836e9 100644
--- a/android/socket.c
+++ b/android/socket.c
@@ -360,6 +360,7 @@
uuid_t uuid;
sdp_uuid128_create(&uuid, app_uuid);
+ sdp_uuid128_to_uuid(&uuid);
record = create_rfcomm_record(chan, &uuid, svc_name, false);
if (!record)
diff --git a/android/system-emulator.c b/android/system-emulator.c
index c1b1b25..4d41fce 100644
--- a/android/system-emulator.c
+++ b/android/system-emulator.c
@@ -47,14 +47,10 @@
static pid_t daemon_pid = -1;
static pid_t snoop_pid = -1;
-static void ctl_start(void)
+static void run_valgrind(char *prg_name)
{
- char prg_name[PATH_MAX + 1];
char *prg_argv[6];
char *prg_envp[3];
- pid_t pid;
-
- snprintf(prg_name, sizeof(prg_name), "%s/%s", exec_dir, "bluetoothd");
prg_argv[0] = "/usr/bin/valgrind";
prg_argv[1] = "--leak-check=full";
@@ -67,6 +63,30 @@
prg_envp[1] = "G_DEBUG=gc-friendly";
prg_envp[2] = NULL;
+ execve(prg_argv[0], prg_argv, prg_envp);
+}
+
+static void run_bluetoothd(char *prg_name)
+{
+ char *prg_argv[3];
+ char *prg_envp[1];
+
+ prg_argv[0] = prg_name;
+ prg_argv[1] = "-d";
+ prg_argv[2] = NULL;
+
+ prg_envp[0] = NULL;
+
+ execve(prg_argv[0], prg_argv, prg_envp);
+}
+
+static void ctl_start(void)
+{
+ char prg_name[PATH_MAX + 1];
+ pid_t pid;
+
+ snprintf(prg_name, sizeof(prg_name), "%s/%s", exec_dir, "bluetoothd");
+
printf("Starting %s\n", prg_name);
pid = fork();
@@ -76,7 +96,10 @@
}
if (pid == 0) {
- execve(prg_argv[0], prg_argv, prg_envp);
+ run_valgrind(prg_name);
+
+ /* Fallback to no valgrind if running with valgind failed */
+ run_bluetoothd(prg_name);
exit(0);
}
diff --git a/attrib/att.c b/attrib/att.c
index 8e9c06d..2680458 100644
--- a/attrib/att.c
+++ b/attrib/att.c
@@ -561,6 +561,64 @@
return len;
}
+uint16_t enc_signed_write_cmd(uint16_t handle, const uint8_t *value,
+ size_t vlen, struct bt_crypto *crypto,
+ const uint8_t csrk[16],
+ uint32_t sign_cnt,
+ uint8_t *pdu, size_t len)
+{
+ const uint16_t hdr_len = sizeof(pdu[0]) + sizeof(handle);
+ const uint16_t min_len = hdr_len + ATT_SIGNATURE_LEN;
+
+ if (pdu == NULL)
+ return 0;
+
+ if (vlen > len - min_len)
+ vlen = len - min_len;
+
+ pdu[0] = ATT_OP_SIGNED_WRITE_CMD;
+ put_le16(handle, &pdu[1]);
+
+ if (vlen > 0)
+ memcpy(&pdu[hdr_len], value, vlen);
+
+ if (!bt_crypto_sign_att(crypto, csrk, pdu, hdr_len + vlen, sign_cnt,
+ &pdu[hdr_len + vlen]))
+ return 0;
+
+ return min_len + vlen;
+}
+
+uint16_t dec_signed_write_cmd(const uint8_t *pdu, size_t len,
+ uint16_t *handle,
+ uint8_t *value, size_t *vlen,
+ uint8_t signature[12])
+{
+ const uint16_t hdr_len = sizeof(pdu[0]) + sizeof(*handle);
+ const uint16_t min_len = hdr_len + ATT_SIGNATURE_LEN;
+
+
+ if (pdu == NULL)
+ return 0;
+
+ if (value == NULL || vlen == NULL || handle == NULL)
+ return 0;
+
+ if (len < min_len)
+ return 0;
+
+ if (pdu[0] != ATT_OP_SIGNED_WRITE_CMD)
+ return 0;
+
+ *vlen = len - min_len;
+ *handle = get_le16(&pdu[1]);
+ memcpy(value, pdu + hdr_len, *vlen);
+
+ memcpy(signature, pdu + hdr_len + *vlen, ATT_SIGNATURE_LEN);
+
+ return len;
+}
+
uint16_t enc_write_req(uint16_t handle, const uint8_t *value, size_t vlen,
uint8_t *pdu, size_t len)
{
diff --git a/attrib/att.h b/attrib/att.h
index c612d80..2311aaf 100644
--- a/attrib/att.h
+++ b/attrib/att.h
@@ -22,6 +22,11 @@
*
*/
+#include "src/shared/crypto.h"
+
+/* Len of signature in write signed packet */
+#define ATT_SIGNATURE_LEN 12
+
/* Attribute Protocol Opcodes */
#define ATT_OP_ERROR 0x01
#define ATT_OP_MTU_REQ 0x02
@@ -83,8 +88,8 @@
#define ATT_PSM 31
/* Flags for Execute Write Request Operation */
-#define ATT_CANCEL_ALL_PREP_WRITES 0x00
-#define ATT_WRITE_ALL_PREP_WRITES 0x01
+#define ATT_CANCEL_ALL_PREP_WRITES 0x00
+#define ATT_WRITE_ALL_PREP_WRITES 0x01
/* Find Information Response Formats */
#define ATT_FIND_INFO_RESP_FMT_16BIT 0x01
@@ -129,6 +134,16 @@
uint8_t *pdu, size_t len);
uint16_t dec_write_cmd(const uint8_t *pdu, size_t len, uint16_t *handle,
uint8_t *value, size_t *vlen);
+uint16_t enc_signed_write_cmd(uint16_t handle,
+ const uint8_t *value, size_t vlen,
+ struct bt_crypto *crypto,
+ const uint8_t csrk[16],
+ uint32_t sign_cnt,
+ uint8_t *pdu, size_t len);
+uint16_t dec_signed_write_cmd(const uint8_t *pdu, size_t len,
+ uint16_t *handle,
+ uint8_t *value, size_t *vlen,
+ uint8_t signature[12]);
struct att_data_list *dec_read_by_type_resp(const uint8_t *pdu, size_t len);
uint16_t enc_write_req(uint16_t handle, const uint8_t *value, size_t vlen,
uint8_t *pdu, size_t len);
diff --git a/attrib/gatt.c b/attrib/gatt.c
index 73eaf7a..27fb0b3 100644
--- a/attrib/gatt.c
+++ b/attrib/gatt.c
@@ -1065,6 +1065,27 @@
return g_attrib_send(attrib, 0, buf, plen, NULL, user_data, notify);
}
+guint gatt_signed_write_cmd(GAttrib *attrib, uint16_t handle,
+ const uint8_t *value, int vlen,
+ struct bt_crypto *crypto,
+ const uint8_t csrk[16],
+ uint32_t sign_cnt,
+ GDestroyNotify notify,
+ gpointer user_data)
+{
+ uint8_t *buf;
+ size_t buflen;
+ guint16 plen;
+
+ buf = g_attrib_get_buffer(attrib, &buflen);
+ plen = enc_signed_write_cmd(handle, value, vlen, crypto, csrk, sign_cnt,
+ buf, buflen);
+ if (plen == 0)
+ return 0;
+
+ return g_attrib_send(attrib, 0, buf, plen, NULL, user_data, notify);
+}
+
static sdp_data_t *proto_seq_find(sdp_list_t *proto_list)
{
sdp_list_t *list;
diff --git a/attrib/gatt.h b/attrib/gatt.h
index 7d055f0..f6db10f 100644
--- a/attrib/gatt.h
+++ b/attrib/gatt.h
@@ -105,6 +105,13 @@
guint gatt_write_cmd(GAttrib *attrib, uint16_t handle, const uint8_t *value,
int vlen, GDestroyNotify notify, gpointer user_data);
+guint gatt_signed_write_cmd(GAttrib *attrib, uint16_t handle,
+ const uint8_t *value, int vlen,
+ struct bt_crypto *crypto,
+ const uint8_t csrk[16],
+ uint32_t sign_cnt,
+ GDestroyNotify notify,
+ gpointer user_data);
guint gatt_read_char_by_uuid(GAttrib *attrib, uint16_t start, uint16_t end,
bt_uuid_t *uuid, GAttribResultFunc func,
gpointer user_data);
diff --git a/btio/btio.c b/btio/btio.c
index 5f91171..50760f0 100644
--- a/btio/btio.c
+++ b/btio/btio.c
@@ -1085,9 +1085,10 @@
break;
}
+ len = sizeof(l2o.omtu);
if (getsockopt(sock, SOL_BLUETOOTH, BT_SNDMTU,
&l2o.omtu, &len) < 0) {
- ERROR_FAILED(err, "getsockopt(BT_RCVMTU)",
+ ERROR_FAILED(err, "getsockopt(BT_SNDMTU)",
errno);
return FALSE;
}
diff --git a/configure.ac b/configure.ac
index d858ff6..fb6b502 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,5 +1,5 @@
AC_PREREQ(2.60)
-AC_INIT(bluez, 5.19)
+AC_INIT(bluez, 5.20)
AM_INIT_AUTOMAKE([foreign subdir-objects color-tests silent-rules
tar-pax no-dist-gzip dist-xz])
@@ -125,8 +125,8 @@
AC_ARG_ENABLE(udev, AC_HELP_STRING([--disable-udev],
[disable udev device support]), [enable_udev=${enableval}])
if (test "${enable_tools}" != "no" && test "${enable_udev}" != "no"); then
- PKG_CHECK_MODULES(UDEV, libudev >= 143, dummy=yes,
- AC_MSG_ERROR(libudev >= 143 is required))
+ PKG_CHECK_MODULES(UDEV, libudev >= 172, dummy=yes,
+ AC_MSG_ERROR(libudev >= 172 is required))
AC_SUBST(UDEV_CFLAGS)
AC_SUBST(UDEV_LIBS)
AC_CHECK_LIB(udev, udev_hwdb_new,
diff --git a/doc/device-api.txt b/doc/device-api.txt
index 2d9fa3c..577ee60 100644
--- a/doc/device-api.txt
+++ b/doc/device-api.txt
@@ -192,4 +192,4 @@
int16 RSSI [readonly, optional]
Received Signal Strength Indicator of the remote
- device.
+ device (inquiry or advertising).
diff --git a/doc/mgmt-api.txt b/doc/mgmt-api.txt
index b691ae0..e15a78f 100644
--- a/doc/mgmt-api.txt
+++ b/doc/mgmt-api.txt
@@ -19,7 +19,8 @@
Linux kernel v3.7 Version 1.2
Linux kernel v3.9 Version 1.3
Linux kernel v3.13 Version 1.4
-Linux kernel v3.15 Version 1.5 (not yet released)
+Linux kernel v3.15 Version 1.5
+Linux kernel v3.16 Version 1.6 (not yet released)
Version 1.1 introduces Set Device ID command.
@@ -34,6 +35,10 @@
and Load Identity Resolving Keys commands. It also introduces New Identity
Resolving Key and New Signature Resolving Key events.
+Version 1.6 introduces Get Connection Information command. It also updates
+the Device Found event to combine advertising data and scan response data
+into a single event.
+
Example
=======
@@ -214,20 +219,20 @@
Current_Settings & Supported_Settings is a bitmask with
currently the following available bits:
- 1 Powered
- 2 Connectable
- 3 Fast Connectable
- 4 Discoverable
- 5 Pairable
- 6 Link Level Security (Sec. mode 3)
- 7 Secure Simple Pairing
- 8 Basic Rate/Enhanced Data Rate
- 9 High Speed
- 10 Low Energy
- 11 Advertising
- 12 Secure Connections
- 13 Debug Keys
- 14 Privacy
+ 0 Powered
+ 1 Connectable
+ 2 Fast Connectable
+ 3 Discoverable
+ 4 Pairable
+ 5 Link Level Security (Sec. mode 3)
+ 6 Secure Simple Pairing
+ 7 Basic Rate/Enhanced Data Rate
+ 8 High Speed
+ 9 Low Energy
+ 10 Advertising
+ 11 Secure Connections
+ 12 Debug Keys
+ 13 Privacy
This command generates a Command Complete event on success or
a Command Status event on failure.
@@ -910,9 +915,18 @@
Return Parameters:
This command is used to set the IO Capability used for pairing.
- The command accepts both SSP and SMP values. The KeyboardDisplay
- SMP value (which doesn't exist for SSP will) automatically be
- downgraded to DisplayYesNo by the kernel for SSP pairing events.
+ The command accepts both SSP and SMP values.
+
+ Possible values for the IO_Capability parameter:
+ 0 DisplayOnly
+ 1 DisplayYesNo
+ 2 KeyboardOnly
+ 3 NoInputNoOutput
+ 4 KeyboardDisplay
+
+ Passing a value 4 (KeyboardDisplay) will cause the kernel to
+ convert it to 1 (DisplayYesNo) in the case of a BR/EDR
+ connection (as KeyboardDisplay is specific to SMP).
This command can be used when the controller is not powered.
@@ -944,6 +958,17 @@
1 LE Public
2 LE Random
+ Possible values for the IO_Capability parameter:
+ 0 DisplayOnly
+ 1 DisplayYesNo
+ 2 KeyboardOnly
+ 3 NoInputNoOutput
+ 4 KeyboardDisplay
+
+ Passing a value 4 (KeyboardDisplay) will cause the kernel to
+ convert it to 1 (DisplayYesNo) in the case of a BR/EDR
+ connection (as KeyboardDisplay is specific to SMP).
+
The Address and Address_Type of the return parameters will
return the identity address if know. In case of resolvable
random address given as command parameters and the remote
@@ -1270,9 +1295,9 @@
Possible values for the Address_Type parameter are a bit-wise or
of the following bits:
- 1 BR/EDR
- 2 LE Public
- 3 LE Random
+ 0 BR/EDR
+ 1 LE Public
+ 2 LE Random
By combining these e.g. the following values are possible:
diff --git a/doc/test-coverage.txt b/doc/test-coverage.txt
index 7b05ff6..f70a8d5 100644
--- a/doc/test-coverage.txt
+++ b/doc/test-coverage.txt
@@ -13,13 +13,15 @@
test-sdp 133 SDP qualification test cases
test-uuid 30 UUID conversion handling
test-mgmt 2 Management interface handling
+test-crypto 4 Cryptographic toolbox helpers
test-textfile 4 Old textfile storage format
test-ringbuf 3 Ring buffer functionality
test-queue 1 Queue handling functionality
-test-hfp 13 HFP Audio Gateway functionality
+test-uhid 6 Userspace HID functionality
+test-hfp 14 HFP Audio Gateway functionality
test-avdtp 60 AVDTP qualification test cases
test-avctp 9 AVCTP qualification test cases
-test-avrcp 90 AVRCP qualification test cases
+test-avrcp 92 AVRCP qualification test cases
test-gobex 31 Generic OBEX functionality
test-gobex-packet 9 OBEX packet handling
test-gobex-header 28 OBEX header handling
@@ -27,7 +29,7 @@
test-gobex-transfer 36 OBEX transfer handling
test-gdbus-client 12 D-Bus client handling
-----
- 506
+ 519
Automated end-to-end testing
@@ -35,7 +37,7 @@
Application Count Description
-------------------------------------------
-mgmt-tester 184 Kernel management interface testing
+mgmt-tester 189 Kernel management interface testing
l2cap-tester 26 Kernel L2CAP implementation testing
rfcomm-tester 9 Kernel RFCOMM implementation testing
smp-tester 5 Kernel SMP implementation testing
@@ -43,7 +45,7 @@
gap-tester 1 Daemon D-Bus API testing
hci-tester 14 Controller hardware testing
-----
- 247
+ 252
Android end-to-end testing
diff --git a/emulator/btdev.c b/emulator/btdev.c
index e590efa..641edea 100644
--- a/emulator/btdev.c
+++ b/emulator/btdev.c
@@ -223,6 +223,29 @@
return NULL;
}
+static inline struct btdev *find_btdev_by_bdaddr_type(const uint8_t *bdaddr,
+ uint8_t bdaddr_type)
+{
+ int i;
+
+ for (i = 0; i < MAX_BTDEV_ENTRIES; i++) {
+ int cmp;
+
+ if (!btdev_list[i])
+ continue;
+
+ if (bdaddr_type == 0x01)
+ cmp = memcmp(btdev_list[i]->random_addr, bdaddr, 6);
+ else
+ cmp = memcmp(btdev_list[i]->bdaddr, bdaddr, 6);
+
+ if (!cmp)
+ return btdev_list[i];
+ }
+
+ return NULL;
+}
+
static void hexdump(const unsigned char *buf, uint16_t len)
{
static const char hexdigits[] = "0123456789abcdef";
@@ -981,7 +1004,8 @@
}
static void le_conn_complete(struct btdev *btdev,
- const uint8_t *bdaddr, uint8_t status)
+ const uint8_t *bdaddr, uint8_t bdaddr_type,
+ uint8_t status)
{
char buf[1 + sizeof(struct bt_hci_evt_le_conn_complete)];
struct bt_hci_evt_le_conn_complete *cc = (void *) &buf[1];
@@ -991,13 +1015,18 @@
buf[0] = BT_HCI_EVT_LE_CONN_COMPLETE;
if (!status) {
- struct btdev *remote = find_btdev_by_bdaddr(bdaddr);
+ struct btdev *remote = find_btdev_by_bdaddr_type(bdaddr,
+ bdaddr_type);
btdev->conn = remote;
remote->conn = btdev;
cc->status = status;
- memcpy(cc->peer_addr, btdev->bdaddr, 6);
+ cc->peer_addr_type = btdev->le_scan_own_addr_type;
+ if (cc->peer_addr_type == 0x01)
+ memcpy(cc->peer_addr, btdev->random_addr, 6);
+ else
+ memcpy(cc->peer_addr, btdev->bdaddr, 6);
cc->role = 0x01;
cc->handle = cpu_to_le16(42);
@@ -1008,6 +1037,7 @@
}
cc->status = status;
+ cc->peer_addr_type = bdaddr_type;
memcpy(cc->peer_addr, bdaddr, 6);
cc->role = 0x00;
@@ -1050,14 +1080,15 @@
return btdev->le_adv_type != 0x03;
}
-static void le_conn_request(struct btdev *btdev, const uint8_t *bdaddr)
+static void le_conn_request(struct btdev *btdev, const uint8_t *bdaddr,
+ uint8_t bdaddr_type)
{
- struct btdev *remote = find_btdev_by_bdaddr(bdaddr);
+ struct btdev *remote = find_btdev_by_bdaddr_type(bdaddr, bdaddr_type);
if (remote && adv_connectable(remote) && adv_match(btdev, remote))
- le_conn_complete(btdev, bdaddr, 0);
+ le_conn_complete(btdev, bdaddr, bdaddr_type, 0);
else
- le_conn_complete(btdev, bdaddr,
+ le_conn_complete(btdev, bdaddr, bdaddr_type,
BT_HCI_ERR_CONN_FAILED_TO_ESTABLISH);
}
@@ -1763,6 +1794,8 @@
const struct bt_hci_cmd_le_start_encrypt *lse;
const struct bt_hci_cmd_le_ltk_req_reply *llrr;
const struct bt_hci_cmd_read_local_amp_assoc *rlaa_cmd;
+ const struct bt_hci_cmd_read_rssi *rrssi;
+ const struct bt_hci_cmd_read_tx_power *rtxp;
struct bt_hci_rsp_read_default_link_policy rdlp;
struct bt_hci_rsp_read_stored_link_key rslk;
struct bt_hci_rsp_write_stored_link_key wslk;
@@ -1813,6 +1846,8 @@
struct bt_hci_rsp_pin_code_request_neg_reply pcrnr_rsp;
struct bt_hci_rsp_user_confirm_request_reply ucrr_rsp;
struct bt_hci_rsp_user_confirm_request_neg_reply ucrnr_rsp;
+ struct bt_hci_rsp_read_rssi rrssi_rsp;
+ struct bt_hci_rsp_read_tx_power rtxp_rsp;
uint8_t status, page;
switch (opcode) {
@@ -2465,6 +2500,39 @@
cmd_complete(btdev, opcode, &rdbs, sizeof(rdbs));
break;
+ case BT_HCI_CMD_READ_RSSI:
+ rrssi = data;
+
+ rrssi_rsp.status = BT_HCI_ERR_SUCCESS;
+ rrssi_rsp.handle = rrssi->handle;
+ rrssi_rsp.rssi = -1; /* non-zero so we can see it in tester */
+ cmd_complete(btdev, opcode, &rrssi_rsp, sizeof(rrssi_rsp));
+ break;
+
+ case BT_HCI_CMD_READ_TX_POWER:
+ rtxp = data;
+
+ switch (rtxp->type) {
+ case 0x00:
+ rtxp_rsp.status = BT_HCI_ERR_SUCCESS;
+ rtxp_rsp.level = -1; /* non-zero */
+ break;
+
+ case 0x01:
+ rtxp_rsp.status = BT_HCI_ERR_SUCCESS;
+ rtxp_rsp.level = 4; /* max for class 2 radio */
+ break;
+
+ default:
+ rtxp_rsp.level = 0;
+ rtxp_rsp.status = BT_HCI_ERR_INVALID_PARAMETERS;
+ break;
+ }
+
+ rtxp_rsp.handle = rtxp->handle;
+ cmd_complete(btdev, opcode, &rtxp_rsp, sizeof(rtxp_rsp));
+ break;
+
case BT_HCI_CMD_READ_LOCAL_AMP_INFO:
if (btdev->type != BTDEV_TYPE_AMP)
goto unsupported;
@@ -2907,7 +2975,7 @@
return;
lecc = data;
btdev->le_scan_own_addr_type = lecc->own_addr_type;
- le_conn_request(btdev, lecc->peer_addr);
+ le_conn_request(btdev, lecc->peer_addr, lecc->peer_addr_type);
break;
}
}
diff --git a/lib/bluetooth.c b/lib/bluetooth.c
index 406b508..8ee6fb4 100644
--- a/lib/bluetooth.c
+++ b/lib/bluetooth.c
@@ -336,7 +336,7 @@
case 36:
return "Alcatel";
case 37:
- return "Philips Semiconductors";
+ return "NXP Semiconductors (formerly Philips Semiconductors)";
case 38:
return "C Technologies";
case 39:
@@ -542,7 +542,7 @@
case 139:
return "Topcorn Positioning Systems, LLC";
case 140:
- return "Qualcomm Retail Solutions, Inc. (formerly Qualcomm Labs, Inc.)";
+ return "Gimbal Inc. (formerly Qualcomm Labs, Inc. and Qualcomm Retail Solutions, Inc.)";
case 141:
return "Zscan Software";
case 142:
@@ -928,11 +928,35 @@
case 332:
return "Mesh-Net Ltd";
case 333:
- return "HUIZHOU DESAY SV AUTOMOTIVE CO., LTD.";
+ return "Huizhou Desay SV Automotive CO., LTD.";
case 334:
return "Tangerine, Inc.";
case 335:
return "B&W Group Ltd.";
+ case 336:
+ return "Pioneer Corporation";
+ case 337:
+ return "OnBeep";
+ case 338:
+ return "Vernier Software & Technology";
+ case 339:
+ return "ROL Ergo";
+ case 340:
+ return "Pebble Technology";
+ case 341:
+ return "NETATMO";
+ case 342:
+ return "Accumulate AB";
+ case 343:
+ return "Anhui Huami Information Technology Co., Ltd.";
+ case 344:
+ return "Inmite s.r.o.";
+ case 345:
+ return "ChefSteps, Inc.";
+ case 346:
+ return "micus AG";
+ case 347:
+ return "Biomedical Research Ltd.";
case 65535:
return "internal use";
default:
diff --git a/lib/sdp.h b/lib/sdp.h
index 34adf9a..cc10e9f 100644
--- a/lib/sdp.h
+++ b/lib/sdp.h
@@ -134,6 +134,8 @@
#define MAP_SVCLASS_ID 0x1134
#define GNSS_SVCLASS_ID 0x1135
#define GNSS_SERVER_SVCLASS_ID 0x1136
+#define MPS_SC_SVCLASS_ID 0x113A
+#define MPS_SVCLASS_ID 0x113B
#define PNP_INFO_SVCLASS_ID 0x1200
#define GENERIC_NETWORKING_SVCLASS_ID 0x1201
#define GENERIC_FILETRANS_SVCLASS_ID 0x1202
@@ -228,6 +230,7 @@
#define GENERIC_ACCESS_PROFILE_ID GENERIC_ACCESS_SVCLASS_ID
#define GENERIC_ATTRIB_PROFILE_ID GENERIC_ATTRIB_SVCLASS_ID
#define APPLE_AGENT_PROFILE_ID APPLE_AGENT_SVCLASS_ID
+#define MPS_PROFILE_ID MPS_SC_SVCLASS_ID
/*
* Compatibility macros for the old MDP acronym
@@ -270,6 +273,10 @@
#define SDP_ATTR_GOEP_L2CAP_PSM 0x0200
#define SDP_ATTR_SVCDB_STATE 0x0201
+#define SDP_ATTR_MPSD_SCENARIOS 0x0200
+#define SDP_ATTR_MPMD_SCENARIOS 0x0201
+#define SDP_ATTR_MPS_DEPENDENCIES 0x0202
+
#define SDP_ATTR_SERVICE_VERSION 0x0300
#define SDP_ATTR_EXTERNAL_NETWORK 0x0301
#define SDP_ATTR_SUPPORTED_DATA_STORES_LIST 0x0301
diff --git a/monitor/control.c b/monitor/control.c
index 8197813..c808a84 100644
--- a/monitor/control.c
+++ b/monitor/control.c
@@ -345,7 +345,7 @@
ba2str(&ev->addr.bdaddr, str);
- printf("@ PIN User Passkey Request: %s (%d)\n", str, ev->addr.type);
+ printf("@ User Passkey Request: %s (%d)\n", str, ev->addr.type);
buf += sizeof(*ev);
len -= sizeof(*ev);
diff --git a/monitor/sdp.c b/monitor/sdp.c
index a0ab314..8acc8bd 100644
--- a/monitor/sdp.c
+++ b/monitor/sdp.c
@@ -737,13 +737,13 @@
print_indent(6, pdu_color, "SDP: ", pdu_str, COLOR_OFF,
" (0x%2.2x) tid %d len %d", pdu, tid, plen);
- if (!sdp_data || !sdp_data->func) {
+ tid_info = get_tid(tid, channel);
+
+ if (!sdp_data || !sdp_data->func || !tid_info) {
packet_hexdump(frame->data + 5, frame->size - 5);
return;
}
- tid_info = get_tid(tid, channel);
-
l2cap_frame_pull(&sdp_frame, frame, 5);
sdp_data->func(&sdp_frame, tid_info);
}
diff --git a/plugins/sixaxis.c b/plugins/sixaxis.c
index 1b7bb30..b09404a 100644
--- a/plugins/sixaxis.c
+++ b/plugins/sixaxis.c
@@ -44,6 +44,7 @@
#include "src/device.h"
#include "src/plugin.h"
#include "src/log.h"
+#include "src/shared/util.h"
static const struct {
const char *name;
@@ -61,6 +62,17 @@
},
};
+struct leds_data {
+ char *syspath_prefix;
+ uint8_t bitmap;
+};
+
+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;
@@ -178,23 +190,58 @@
error("sixaxis: failed to set LEDS (%d bytes written)", ret);
}
+static bool set_leds_sysfs(struct leds_data *data)
+{
+ int i;
+
+ if (!data->syspath_prefix)
+ return false;
+
+ /* 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;
+
+ snprintf(path, PATH_MAX, "%s%d/brightness",
+ data->syspath_prefix, i);
+
+ fd = open(path, O_WRONLY);
+ if (fd < 0) {
+ error("sixaxis: cannot open %s (%s)", path,
+ strerror(errno));
+ return false;
+ }
+
+ buf[0] = '0' + !!(data->bitmap & (1 << i));
+ ret = write(fd, buf, sizeof(buf));
+ close(fd);
+ if (ret != sizeof(buf))
+ return false;
+ }
+
+ return true;
+}
+
static gboolean setup_leds(GIOChannel *channel, GIOCondition cond,
gpointer user_data)
{
- int number = GPOINTER_TO_INT(user_data);
- uint8_t bitmap;
- int fd;
+ struct leds_data *data = user_data;
- if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL))
+ if (!data)
return FALSE;
- DBG("number %d", number);
+ if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL))
+ goto out;
- fd = g_io_channel_unix_get_fd(channel);
+ if(!set_leds_sysfs(data)) {
+ int fd = g_io_channel_unix_get_fd(channel);
+ set_leds_hidraw(fd, data->bitmap);
+ }
- bitmap = calc_leds_bitmap(number);
- if (bitmap != 0)
- set_leds_hidraw(fd, bitmap);
+out:
+ leds_data_destroy(data);
return FALSE;
}
@@ -257,15 +304,24 @@
struct udev_enumerate *enumerate;
struct udev_device *hid_parent;
const char *hidraw_node;
- const char *hid_phys;
+ const char *hid_id;
int number = 0;
hid_parent = udev_device_get_parent_with_subsystem_devtype(udevice,
"hid", NULL);
- hid_phys = udev_device_get_property_value(hid_parent, "HID_PHYS");
+ /*
+ * 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_phys || !hidraw_node)
+ if (!hid_id || !hidraw_node)
return 0;
enumerate = udev_enumerate_new(udev_device_get_udev(udevice));
@@ -276,7 +332,7 @@
udev_list_entry_foreach(dev_list_entry, devices) {
struct udev_device *input_parent;
struct udev_device *js_dev;
- const char *input_phys;
+ const char *input_id;
const char *devname;
devname = udev_list_entry_get_name(dev_list_entry);
@@ -291,12 +347,20 @@
/* check if this is the joystick relative to the hidraw device
* above */
- input_phys = udev_device_get_sysattr_value(input_parent,
- "phys");
- if (!input_phys)
+ 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_phys, hid_phys)) {
+ if (!strcmp(input_id, hid_id)) {
number = atoi(udev_device_get_sysnum(js_dev));
/* joystick numbers start from 0, leds from 1 */
@@ -314,6 +378,72 @@
return number;
}
+static char *get_leds_syspath_prefix(struct udev_device *udevice)
+{
+ 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);
+
+ 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);
+
+ dev_list_entry = udev_enumerate_get_list_entry(enumerate);
+ if (!dev_list_entry) {
+ syspath_prefix = NULL;
+ goto out;
+ }
+
+ 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;
+
+ number = get_js_number(udevice);
+ DBG("number %d", number);
+
+ data = malloc0(sizeof(*data));
+ if (!data)
+ return NULL;
+
+ data->bitmap = calc_leds_bitmap(number);
+ if (data->bitmap == 0) {
+ leds_data_destroy(data);
+ return NULL;
+ }
+
+ /*
+ * 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);
+
+ return data;
+}
+
static int get_supported_device(struct udev_device *udevice, uint16_t *bus)
{
struct udev_device *hid_parent;
@@ -374,8 +504,7 @@
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,
- GINT_TO_POINTER(get_js_number(udevice)));
+ setup_leds, get_leds_data(udevice));
break;
default:
diff --git a/profiles/audio/a2dp-codecs.h b/profiles/audio/a2dp-codecs.h
index 3dc31cb..4d2584d 100644
--- a/profiles/audio/a2dp-codecs.h
+++ b/profiles/audio/a2dp-codecs.h
@@ -85,6 +85,67 @@
#define MPEG_BIT_RATE_32000 0x0002
#define MPEG_BIT_RATE_FREE 0x0001
+#define AAC_OBJECT_TYPE_MPEG2_AAC_LC 0x80
+#define AAC_OBJECT_TYPE_MPEG4_AAC_LC 0x40
+#define AAC_OBJECT_TYPE_MPEG4_AAC_LTP 0x20
+#define AAC_OBJECT_TYPE_MPEG4_AAC_SCA 0x10
+
+#define AAC_SAMPLING_FREQ_8000 0x0800
+#define AAC_SAMPLING_FREQ_11025 0x0400
+#define AAC_SAMPLING_FREQ_12000 0x0200
+#define AAC_SAMPLING_FREQ_16000 0x0100
+#define AAC_SAMPLING_FREQ_22050 0x0080
+#define AAC_SAMPLING_FREQ_24000 0x0040
+#define AAC_SAMPLING_FREQ_32000 0x0020
+#define AAC_SAMPLING_FREQ_44100 0x0010
+#define AAC_SAMPLING_FREQ_48000 0x0008
+#define AAC_SAMPLING_FREQ_64000 0x0004
+#define AAC_SAMPLING_FREQ_88200 0x0002
+#define AAC_SAMPLING_FREQ_96000 0x0001
+
+#define AAC_CHANNELS_1 0x02
+#define AAC_CHANNELS_2 0x01
+
+#define AAC_GET_BITRATE(a) ((a).bitrate1 << 16 | \
+ (a).bitrate2 << 8 | (a).bitrate3)
+#define AAC_GET_FREQUENCY(a) ((a).frequency1 << 4 | (a).frequency2)
+
+#define AAC_SET_BITRATE(a, b) \
+ do { \
+ (a).bitrate1 = (b >> 16) & 0x7f; \
+ (a).bitrate2 = (b >> 8) & 0xff; \
+ (a).bitrate3 = b & 0xff; \
+ } while (0)
+#define AAC_SET_FREQUENCY(a, f) \
+ do { \
+ (a).frequency1 = (f >> 4) & 0xff; \
+ (a).frequency2 = f & 0x0f; \
+ } while (0)
+
+#define AAC_INIT_BITRATE(b) \
+ .bitrate1 = (b >> 16) & 0x7f, \
+ .bitrate2 = (b >> 8) & 0xff, \
+ .bitrate3 = b & 0xff,
+#define AAC_INIT_FREQUENCY(f) \
+ .frequency1 = (f >> 4) & 0xff, \
+ .frequency2 = f & 0x0f,
+
+#define APTX_VENDOR_ID 0x0000004f
+#define APTX_CODEC_ID 0x0001
+
+#define APTX_CHANNEL_MODE_MONO 0x01
+#define APTX_CHANNEL_MODE_STEREO 0x02
+
+#define APTX_SAMPLING_FREQ_16000 0x08
+#define APTX_SAMPLING_FREQ_32000 0x04
+#define APTX_SAMPLING_FREQ_44100 0x02
+#define APTX_SAMPLING_FREQ_48000 0x01
+
+typedef struct {
+ uint32_t vendor_id;
+ uint16_t codec_id;
+} __attribute__ ((packed)) a2dp_vendor_codec_t;
+
#if __BYTE_ORDER == __LITTLE_ENDIAN
typedef struct {
@@ -107,6 +168,24 @@
uint16_t bitrate;
} __attribute__ ((packed)) a2dp_mpeg_t;
+typedef struct {
+ uint8_t object_type;
+ uint8_t frequency1;
+ uint8_t rfa:2;
+ uint8_t channels:2;
+ uint8_t frequency2:4;
+ uint8_t bitrate1:7;
+ uint8_t vbr:1;
+ uint8_t bitrate2;
+ uint8_t bitrate3;
+} __attribute__ ((packed)) a2dp_aac_t;
+
+typedef struct {
+ a2dp_vendor_codec_t info;
+ uint8_t channel_mode:4;
+ uint8_t frequency:4;
+} __attribute__ ((packed)) a2dp_aptx_t;
+
#elif __BYTE_ORDER == __BIG_ENDIAN
typedef struct {
@@ -129,11 +208,24 @@
uint16_t bitrate;
} __attribute__ ((packed)) a2dp_mpeg_t;
+typedef struct {
+ uint8_t object_type;
+ uint8_t frequency1;
+ uint8_t frequency2:4;
+ uint8_t channels:2;
+ uint8_t rfa:2;
+ uint8_t vbr:1;
+ uint8_t bitrate1:7;
+ uint8_t bitrate2;
+ uint8_t bitrate3;
+} __attribute__ ((packed)) a2dp_aac_t;
+
+typedef struct {
+ a2dp_vendor_codec_t info;
+ uint8_t frequency:4;
+ uint8_t channel_mode:4;
+} __attribute__ ((packed)) a2dp_aptx_t;
+
#else
#error "Unknown byte order"
#endif
-
-typedef struct {
- uint8_t vendor_id[4];
- uint8_t codec_id[2];
-} __attribute__ ((packed)) a2dp_vendor_codec_t;
diff --git a/profiles/audio/a2dp.c b/profiles/audio/a2dp.c
index cabdd66..c9dac9a 100644
--- a/profiles/audio/a2dp.c
+++ b/profiles/audio/a2dp.c
@@ -1390,18 +1390,14 @@
local_codec = (a2dp_vendor_codec_t *) capabilities;
- if (memcmp(remote_codec->vendor_id, local_codec->vendor_id,
- sizeof(local_codec->vendor_id)))
+ if (btohl(remote_codec->vendor_id) != btohl(local_codec->vendor_id))
return FALSE;
- if (memcmp(remote_codec->codec_id, local_codec->codec_id,
- sizeof(local_codec->codec_id)))
+ if (btohs(remote_codec->codec_id) != btohs(local_codec->codec_id))
return FALSE;
- DBG("vendor 0x%02x%02x%02x%02x codec 0x%02x%02x",
- remote_codec->vendor_id[0], remote_codec->vendor_id[1],
- remote_codec->vendor_id[2], remote_codec->vendor_id[3],
- remote_codec->codec_id[0], remote_codec->codec_id[1]);
+ DBG("vendor 0x%08x codec 0x%04x", btohl(remote_codec->vendor_id),
+ btohs(remote_codec->codec_id));
return TRUE;
}
diff --git a/profiles/audio/media.c b/profiles/audio/media.c
index b71196a..fa5c013 100644
--- a/profiles/audio/media.c
+++ b/profiles/audio/media.c
@@ -1032,6 +1032,8 @@
return "Track";
else if (strcasecmp(value, "alltracks") == 0)
return "Playlist";
+ else if (strcasecmp(value, "group") == 0)
+ return "Playlist";
return NULL;
}
@@ -1057,12 +1059,17 @@
const char *iface = MEDIA_PLAYER_INTERFACE;
DBusMessage *msg;
DBusMessageIter iter;
+ const char *curval;
DBG("%s = %s", key, value);
- if (!g_hash_table_lookup(mp->settings, key))
+ curval = g_hash_table_lookup(mp->settings, key);
+ if (!curval)
return -EINVAL;
+ if (strcasecmp(curval, value) == 0)
+ return 0;
+
msg = dbus_message_new_method_call(mp->sender, mp->path,
DBUS_INTERFACE_PROPERTIES, "Set");
if (msg == NULL) {
diff --git a/profiles/audio/sink.c b/profiles/audio/sink.c
index d16af23..da8992e 100644
--- a/profiles/audio/sink.c
+++ b/profiles/audio/sink.c
@@ -229,11 +229,14 @@
if (err) {
avdtp_unref(sink->session);
sink->session = NULL;
- if (avdtp_error_category(err) == AVDTP_ERRNO
- && avdtp_error_posix_errno(err) != EHOSTDOWN) {
- perr = -EAGAIN;
- } else
- perr = -EIO;
+
+ perr = -avdtp_error_posix_errno(err);
+ if (perr != -EHOSTDOWN) {
+ if (avdtp_error_category(err) == AVDTP_ERRNO)
+ perr = -EAGAIN;
+ else
+ perr = -EIO;
+ }
goto failed;
}
diff --git a/profiles/audio/source.c b/profiles/audio/source.c
index 843b3e8..b0abaa3 100644
--- a/profiles/audio/source.c
+++ b/profiles/audio/source.c
@@ -229,11 +229,14 @@
if (err) {
avdtp_unref(source->session);
source->session = NULL;
- if (avdtp_error_category(err) == AVDTP_ERRNO
- && avdtp_error_posix_errno(err) != EHOSTDOWN) {
- perr = -EAGAIN;
- } else
- perr = -EIO;
+
+ perr = -avdtp_error_posix_errno(err);
+ if (perr != -EHOSTDOWN) {
+ if (avdtp_error_category(err) == AVDTP_ERRNO)
+ perr = -EAGAIN;
+ else
+ perr = -EIO;
+ }
goto failed;
}
diff --git a/profiles/gatt/gas.c b/profiles/gatt/gas.c
index d240450..b51b4a8 100644
--- a/profiles/gatt/gas.c
+++ b/profiles/gatt/gas.c
@@ -329,6 +329,11 @@
DBG("MTU Exchange: Requesting %d", imtu);
}
+ if (gerr) {
+ error("Could not acquire att imtu and cid: %s", gerr->message);
+ g_error_free(gerr);
+ }
+
if (device_get_appearance(gas->device, &app) < 0) {
bt_uuid_t uuid;
diff --git a/profiles/health/hdp.c b/profiles/health/hdp.c
index 48dad52..4f90380 100644
--- a/profiles/health/hdp.c
+++ b/profiles/health/hdp.c
@@ -747,8 +747,11 @@
{
struct hdp_channel *hdp_chann;
- if (dev == NULL)
+ if (dev == NULL) {
+ g_set_error(err, HDP_ERROR, HDP_UNSPECIFIED_ERROR,
+ "HDP device uninitialized");
return NULL;
+ }
hdp_chann = g_new0(struct hdp_channel, 1);
hdp_chann->config = config;
@@ -860,7 +863,10 @@
chan->edata->echo_done = TRUE;
fd = g_io_channel_unix_get_fd(io_chan);
+
len = read(fd, buf, sizeof(buf));
+ if (len < 0)
+ goto fail;
if (send_echo_data(fd, buf, len) >= 0)
return TRUE;
@@ -930,7 +936,8 @@
struct hdp_device *dev = data;
struct hdp_channel *chan;
- DBG("hdp_mcap_mdl_connected_cb");
+ DBG("");
+
if (dev->ndc == NULL)
return;
@@ -1501,8 +1508,8 @@
}
fd = g_io_channel_unix_get_fd(io_chan);
- len = read(fd, buf, sizeof(buf));
+ len = read(fd, buf, sizeof(buf));
if (len != HDP_ECHO_LEN) {
value = FALSE;
goto end;
diff --git a/profiles/health/hdp_util.c b/profiles/health/hdp_util.c
index 58770b5..47c464e 100644
--- a/profiles/health/hdp_util.c
+++ b/profiles/health/hdp_util.c
@@ -652,6 +652,8 @@
return FALSE;
}
+ sdp_list_free(sup_features, free_hdp_list);
+
return TRUE;
}
diff --git a/profiles/input/device.c b/profiles/input/device.c
index ccde452..e37e7b9 100644
--- a/profiles/input/device.c
+++ b/profiles/input/device.c
@@ -52,15 +52,13 @@
#include "src/dbus-common.h"
#include "src/error.h"
#include "src/sdp-client.h"
+#include "src/shared/uhid.h"
#include "device.h"
#include "hidp_defs.h"
-#include "uhid_copy.h"
#define INPUT_INTERFACE "org.bluez.Input1"
-#define UHID_DEVICE_FILE "/dev/uhid"
-
enum reconnect_mode_t {
RECONNECT_NONE = 0,
RECONNECT_DEVICE,
@@ -81,14 +79,11 @@
guint intr_watch;
guint sec_watch;
struct hidp_connadd_req *req;
- guint dc_id;
bool disable_sdp;
enum reconnect_mode_t reconnect_mode;
guint reconnect_timer;
uint32_t reconnect_attempt;
- bool uhid_enabled;
- int uhid_fd;
- guint uhid_watch;
+ struct bt_uhid *uhid;
bool uhid_created;
uint8_t report_req_pending;
guint report_req_timer;
@@ -119,9 +114,7 @@
static void input_device_free(struct input_device *idev)
{
- if (idev->dc_id)
- device_remove_disconnect_watch(idev->device, idev->dc_id);
-
+ bt_uhid_unref(idev->uhid);
btd_service_unref(idev->service);
btd_device_unref(idev->device);
g_free(idev->path);
@@ -204,7 +197,7 @@
uint32_t id, uint16_t err)
{
struct uhid_event ev;
- ssize_t len;
+ int ret;
if (data == NULL)
size = 0;
@@ -226,20 +219,13 @@
if (size > 0)
memcpy(ev.u.feature_answer.data, data, size);
- len = write(idev->uhid_fd, &ev, sizeof(ev));
- if (len < 0) {
- error("uHID dev write error: %s (%d)", strerror(errno), errno);
+ ret = bt_uhid_send(idev->uhid, &ev);
+ if (ret < 0) {
+ error("bt_uhid_send: %s (%d)", strerror(-ret), -ret);
return false;
}
- /* uHID kernel driver does not handle partial writes */
- if ((size_t) len < sizeof(ev)) {
- error("uHID dev write error: partial write (%zd of %zu bytes)",
- len, sizeof(ev));
- return false;
- }
-
- DBG("HID report (%zu bytes) -> uHID fd %d", size, idev->uhid_fd);
+ DBG("HID report (%zu bytes)", size);
return true;
}
@@ -248,7 +234,7 @@
const uint8_t *data, size_t size)
{
struct uhid_event ev;
- ssize_t len;
+ int err;
if (data == NULL)
size = 0;
@@ -268,20 +254,13 @@
if (size > 0)
memcpy(ev.u.input.data, data, size);
- len = write(idev->uhid_fd, &ev, sizeof(ev));
- if (len < 0) {
- error("uHID dev write error: %s (%d)", strerror(errno), errno);
+ err = bt_uhid_send(idev->uhid, &ev);
+ if (err < 0) {
+ error("bt_uhid_send: %s (%d)", strerror(-err), -err);
return false;
}
- /* uHID kernel driver does not handle partial writes */
- if ((size_t) len < sizeof(ev)) {
- error("uHID dev write error: partial write (%zd of %zu bytes)",
- len, sizeof(ev));
- return false;
- }
-
- DBG("HID report (%zu bytes) -> uHID fd %d", size, idev->uhid_fd);
+ DBG("HID report (%zu bytes)", size);
return true;
}
@@ -342,9 +321,6 @@
if ((cond & (G_IO_HUP | G_IO_ERR)) && idev->ctrl_watch)
g_io_channel_shutdown(chan, TRUE, NULL);
- device_remove_disconnect_watch(idev->device, idev->dc_id);
- idev->dc_id = 0;
-
idev->intr_watch = 0;
if (idev->intr_io) {
@@ -586,9 +562,9 @@
return FALSE;
}
-static void hidp_send_set_report(struct input_device *idev,
- struct uhid_event *ev)
+static void hidp_send_set_report(struct uhid_event *ev, void *user_data)
{
+ struct input_device *idev = user_data;
uint8_t hdr;
bool sent;
@@ -624,9 +600,9 @@
}
}
-static void hidp_send_get_report(struct input_device *idev,
- struct uhid_event *ev)
+static void hidp_send_get_report(struct uhid_event *ev, void *user_data)
{
+ struct input_device *idev = user_data;
uint8_t hdr;
bool sent;
@@ -666,91 +642,6 @@
}
}
-static gboolean uhid_watch_cb(GIOChannel *chan, GIOCondition cond,
- gpointer user_data)
-{
- struct input_device *idev = user_data;
- int fd;
- ssize_t len;
- struct uhid_event ev;
-
- if (cond & (G_IO_ERR | G_IO_NVAL))
- goto failed;
-
- fd = g_io_channel_unix_get_fd(chan);
- memset(&ev, 0, sizeof(ev));
-
- len = read(fd, &ev, sizeof(ev));
- if (len < 0) {
- error("uHID dev read error: %s (%d)", strerror(errno), errno);
- goto failed;
- }
-
- if ((size_t) len < sizeof(ev.type)) {
- error("uHID dev read returned too few bytes");
- goto failed;
- }
-
- DBG("uHID event type %u received (%zd bytes)", ev.type, len);
-
- switch (ev.type) {
- case UHID_START:
- case UHID_STOP:
- /* These are called to start and stop the underlying hardware.
- * For HID we open the channels before creating the device so
- * the hardware is always ready. No need to handle these.
- * Note that these are also called when the kernel switches
- * between device-drivers loaded on the HID device. But we can
- * simply keep the hardware alive during transitions and it
- * works just fine.
- * The kernel never destroys a device itself! Only an explicit
- * UHID_DESTROY request can remove a device.
- */
- break;
- case UHID_OPEN:
- case UHID_CLOSE:
- /* OPEN/CLOSE are sent whenever user-space opens any interface
- * provided by the kernel HID device. Whenever the open-count
- * is non-zero we must be ready for I/O. As long as it is zero,
- * we can decide to drop all I/O and put the device
- * asleep This is optional, though. Moreover, some
- * special device drivers are buggy in that regard, so
- * maybe we just keep I/O always awake like HIDP in the
- * kernel does.
- */
- break;
- case UHID_OUTPUT:
- hidp_send_set_report(idev, &ev);
- break;
- case UHID_FEATURE:
- hidp_send_get_report(idev, &ev);
- break;
- case UHID_OUTPUT_EV:
- /* This is only sent by kernels prior to linux-3.11. It
- * requires us to parse HID-descriptors in user-space to
- * properly handle it. This is redundant as the kernel
- * does it already. That's why newer kernels assemble
- * the output-reports and send it to us via UHID_OUTPUT.
- * We never implemented this, so we rely on users to use
- * recent-enough kernels if they want this feature. No reason
- * to implement this for older kernels.
- */
- DBG("Unsupported uHID output event: type %u code %u value %d",
- ev.u.output_ev.type, ev.u.output_ev.code,
- ev.u.output_ev.value);
- break;
- default:
- warn("unexpected uHID event");
- break;
- }
-
- return TRUE;
-
-failed:
- idev->uhid_watch = 0;
- return FALSE;
-}
-
static void epox_endian_quirk(unsigned char *data, int size)
{
/* USAGE_PAGE (Keyboard) 05 07
@@ -955,33 +846,38 @@
static int uhid_connadd(struct input_device *idev, struct hidp_connadd_req *req)
{
- int err = 0;
+ int err;
struct uhid_event ev;
- if (!idev->uhid_created) {
- /* create uHID device */
- memset(&ev, 0, sizeof(ev));
- ev.type = UHID_CREATE;
- strncpy((char *) ev.u.create.name, req->name,
- sizeof(ev.u.create.name) - 1);
- ba2str(&idev->src, (char *) ev.u.create.phys);
- ba2str(&idev->dst, (char *) ev.u.create.uniq);
- ev.u.create.vendor = req->vendor;
- ev.u.create.product = req->product;
- ev.u.create.version = req->version;
- ev.u.create.country = req->country;
- ev.u.create.bus = BUS_BLUETOOTH;
- ev.u.create.rd_data = req->rd_data;
- ev.u.create.rd_size = req->rd_size;
+ if (idev->uhid_created)
+ return 0;
- if (write(idev->uhid_fd, &ev, sizeof(ev)) < 0) {
- err = -errno;
- error("Failed to create uHID device: %s (%d)",
- strerror(-err), -err);
- } else
- idev->uhid_created = true;
+ /* create uHID device */
+ memset(&ev, 0, sizeof(ev));
+ ev.type = UHID_CREATE;
+ strncpy((char *) ev.u.create.name, req->name,
+ sizeof(ev.u.create.name) - 1);
+ ba2str(&idev->src, (char *) ev.u.create.phys);
+ ba2str(&idev->dst, (char *) ev.u.create.uniq);
+ ev.u.create.vendor = req->vendor;
+ ev.u.create.product = req->product;
+ ev.u.create.version = req->version;
+ ev.u.create.country = req->country;
+ ev.u.create.bus = BUS_BLUETOOTH;
+ ev.u.create.rd_data = req->rd_data;
+ ev.u.create.rd_size = req->rd_size;
+
+ err = bt_uhid_send(idev->uhid, &ev);
+ if (err < 0) {
+ error("bt_uhid_send: %s", strerror(-err));
+ return err;
}
+ bt_uhid_register(idev->uhid, UHID_OUTPUT, hidp_send_set_report, idev);
+ bt_uhid_register(idev->uhid, UHID_FEATURE, hidp_send_get_report, idev);
+
+ idev->uhid_created = true;
+
return err;
}
@@ -993,7 +889,7 @@
DBG("");
- if (idev->uhid_enabled)
+ if (idev->uhid)
err = uhid_connadd(idev, idev->req);
else
err = ioctl_connadd(idev->req);
@@ -1095,7 +991,7 @@
return 0;
}
- if (idev->uhid_enabled)
+ if (idev->uhid)
err = uhid_connadd(idev, req);
else
err = ioctl_connadd(req);
@@ -1109,7 +1005,7 @@
static bool is_connected(struct input_device *idev)
{
- if (idev->uhid_enabled)
+ if (idev->uhid)
return (idev->intr_io != NULL && idev->ctrl_io != NULL);
else
return ioctl_is_connected(idev);
@@ -1126,25 +1022,12 @@
if (idev->ctrl_io)
g_io_channel_shutdown(idev->ctrl_io, TRUE, NULL);
- if (idev->uhid_enabled)
+ if (idev->uhid)
return 0;
else
return ioctl_disconnect(idev, flags);
}
-static void disconnect_cb(struct btd_device *device, gboolean removal,
- void *user_data)
-{
- struct input_device *idev = user_data;
- int flags;
-
- info("Input: disconnect %s", idev->path);
-
- flags = removal ? (1 << HIDP_VIRTUAL_CABLE_UNPLUG) : 0;
-
- connection_disconnect(idev, flags);
-}
-
static int input_device_connected(struct input_device *idev)
{
int err;
@@ -1156,9 +1039,6 @@
if (err < 0)
return err;
- idev->dc_id = device_add_disconnect_watch(idev->device, disconnect_cb,
- idev, NULL);
-
btd_service_connecting_complete(idev->service, 0);
return 0;
@@ -1180,7 +1060,7 @@
if (err < 0)
goto failed;
- if (idev->uhid_enabled)
+ if (idev->uhid)
cond |= G_IO_IN;
idev->intr_watch = g_io_add_watch(idev->intr_io, cond, intr_watch_cb,
@@ -1235,7 +1115,7 @@
idev->intr_io = io;
- if (idev->uhid_enabled)
+ if (idev->uhid)
cond |= G_IO_IN;
idev->ctrl_watch = g_io_add_watch(idev->ctrl_io, cond, ctrl_watch_cb,
@@ -1366,13 +1246,16 @@
int input_device_disconnect(struct btd_service *service)
{
struct input_device *idev;
- int err;
+ int err, flags;
DBG("");
idev = btd_service_get_user_data(service);
- err = connection_disconnect(idev, 0);
+ flags = device_is_temporary(idev->device) ?
+ (1 << HIDP_VIRTUAL_CABLE_UNPLUG) : 0;
+
+ err = connection_disconnect(idev, flags);
if (err < 0)
return err;
@@ -1441,7 +1324,6 @@
idev->path = g_strdup(path);
idev->handle = rec->handle;
idev->disable_sdp = is_device_sdp_disable(rec);
- idev->uhid_enabled = uhid_enabled;
/* Initialize device properties */
extract_hid_props(idev, rec);
@@ -1471,8 +1353,6 @@
struct btd_device *device = btd_service_get_device(service);
const char *path = device_get_path(device);
struct input_device *idev;
- int err;
- GIOChannel *io;
DBG("%s", path);
@@ -1480,22 +1360,13 @@
if (!idev)
return -EINVAL;
- if (idev->uhid_enabled) {
- idev->uhid_fd = open(UHID_DEVICE_FILE, O_RDWR | O_CLOEXEC);
- if (idev->uhid_fd < 0) {
- err = errno;
- error("Failed to open uHID device: %s (%d)",
- strerror(err), err);
+ if (uhid_enabled) {
+ idev->uhid = bt_uhid_new_default();
+ if (!idev->uhid) {
+ error("bt_uhid_new_default: failed");
input_device_free(idev);
- return -err;
+ return -EIO;
}
-
- io = g_io_channel_unix_new(idev->uhid_fd);
- g_io_channel_set_encoding(io, NULL, NULL);
- idev->uhid_watch = g_io_add_watch(io,
- G_IO_IN | G_IO_ERR | G_IO_NVAL,
- uhid_watch_cb, idev);
- g_io_channel_unref(io);
}
if (g_dbus_register_interface(btd_get_dbus_connection(),
@@ -1535,31 +1406,12 @@
struct btd_device *device = btd_service_get_device(service);
const char *path = device_get_path(device);
struct input_device *idev = btd_service_get_user_data(service);
- struct uhid_event ev;
DBG("%s", path);
g_dbus_unregister_interface(btd_get_dbus_connection(),
idev->path, INPUT_INTERFACE);
- if (idev->uhid_enabled) {
- if (idev->uhid_watch) {
- g_source_remove(idev->uhid_watch);
- idev->uhid_watch = 0;
- }
-
- if (idev->uhid_created) {
- memset(&ev, 0, sizeof(ev));
- ev.type = UHID_DESTROY;
- if (write(idev->uhid_fd, &ev, sizeof(ev)) < 0)
- error("Failed to destroy uHID device: %s (%d)",
- strerror(errno), errno);
- }
-
- close(idev->uhid_fd);
- idev->uhid_fd = -1;
- }
-
input_device_free(idev);
}
@@ -1605,7 +1457,7 @@
if (!idev)
return -ENOENT;
- if (idev->uhid_enabled)
+ if (uhid_enabled)
cond |= G_IO_IN;
switch (psm) {
diff --git a/profiles/input/hog.c b/profiles/input/hog.c
index e82e827..3753343 100644
--- a/profiles/input/hog.c
+++ b/profiles/input/hog.c
@@ -34,7 +34,6 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
-#include "uhid_copy.h"
#include <bluetooth/bluetooth.h>
@@ -48,6 +47,7 @@
#include "src/profile.h"
#include "src/service.h"
#include "src/shared/util.h"
+#include "src/shared/uhid.h"
#include "src/plugin.h"
@@ -72,8 +72,6 @@
#define HOG_PROTO_MODE_BOOT 0
#define HOG_PROTO_MODE_REPORT 1
-#define UHID_DEVICE_FILE "/dev/uhid"
-
#define HOG_REPORT_MAP_MAX_SIZE 512
#define HID_INFO_SIZE 4
#define ATT_NOTIFICATION_HEADER_SIZE 3
@@ -85,9 +83,8 @@
guint attioid;
struct gatt_primary *hog_primary;
GSList *reports;
- int uhid_fd;
+ struct bt_uhid *uhid;
gboolean has_report_id;
- guint uhid_watch_id;
uint16_t bcdhid;
uint8_t bcountrycode;
uint16_t proto_mode_handle;
@@ -112,7 +109,7 @@
struct hog_device *hogdev = report->hogdev;
struct uhid_event ev;
uint8_t *buf;
- ssize_t status;
+ int err;
if (len < ATT_NOTIFICATION_HEADER_SIZE) {
error("Malformed ATT notification");
@@ -137,21 +134,13 @@
ev.u.input.size = len;
}
- status = write(hogdev->uhid_fd, &ev, sizeof(ev));
- if (status < 0) {
- error("uHID dev write error: %s (%d)", strerror(errno), errno);
+ err = bt_uhid_send(hogdev->uhid, &ev);
+ if (err < 0) {
+ error("bt_uhid_send: %s (%d)", strerror(-err), -err);
return;
}
- /* uHID kernel driver does not handle partial writes */
- if ((size_t) status < sizeof(ev)) {
- error("uHID dev write error: partial write (%zd of %zu bytes)",
- status, sizeof(ev));
- return;
- }
-
- DBG("HoG report (%u bytes) -> uHID fd %d", ev.u.input.size,
- hogdev->uhid_fd);
+ DBG("HoG report (%u bytes)", ev.u.input.size);
}
static void report_ccc_written_cb(guint8 status, const guint8 *pdu,
@@ -317,6 +306,72 @@
external_service_char_cb, hogdev);
}
+static int report_type_cmp(gconstpointer a, gconstpointer b)
+{
+ const struct report *report = a;
+ uint8_t type = GPOINTER_TO_UINT(b);
+
+ return report->type - type;
+}
+
+static void output_written_cb(guint8 status, const guint8 *pdu,
+ guint16 plen, gpointer user_data)
+{
+ 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 hog_device *hogdev = user_data;
+ struct report *report;
+ GSList *l;
+ void *data;
+ int size;
+ guint type;
+
+ if (hogdev->has_report_id) {
+ data = ev->u.output.data + 1;
+ size = ev->u.output.size - 1;
+ } else {
+ data = ev->u.output.data;
+ size = ev->u.output.size;
+ }
+
+ switch (ev->type) {
+ case UHID_OUTPUT:
+ type = HOG_REPORT_TYPE_OUTPUT;
+ break;
+ case UHID_FEATURE:
+ type = HOG_REPORT_TYPE_FEATURE;
+ break;
+ default:
+ return;
+ }
+
+ l = g_slist_find_custom(hogdev->reports, GUINT_TO_POINTER(type),
+ report_type_cmp);
+ if (!l)
+ return;
+
+ report = l->data;
+
+ DBG("Sending report type %d to device 0x%04X handle 0x%X", type,
+ hogdev->id, report->decl->value_handle);
+
+ if (hogdev->attrib == NULL)
+ return;
+
+ if (report->decl->properties & GATT_CHR_PROP_WRITE)
+ gatt_write_char(hogdev->attrib, report->decl->value_handle,
+ data, size, output_written_cb, hogdev);
+ else if (report->decl->properties & GATT_CHR_PROP_WRITE_WITHOUT_RESP)
+ gatt_write_cmd(hogdev->attrib, report->decl->value_handle,
+ data, size, NULL, NULL);
+}
+
static bool get_descriptor_item_info(uint8_t *buf, ssize_t blen, ssize_t *len,
bool *is_long)
{
@@ -389,7 +444,7 @@
uint16_t vendor_src, vendor, product, version;
ssize_t vlen;
char itemstr[20]; /* 5x3 (data) + 4 (continuation) + 1 (null) */
- int i;
+ int i, err;
if (status != 0) {
error("Report Map read failed: %s", att_ecode2str(status));
@@ -450,8 +505,14 @@
ev.u.create.rd_data = value;
ev.u.create.rd_size = vlen;
- if (write(hogdev->uhid_fd, &ev, sizeof(ev)) < 0)
- error("Failed to create uHID device: %s", strerror(errno));
+ err = bt_uhid_send(hogdev->uhid, &ev);
+ if (err < 0) {
+ error("bt_uhid_send: %s", strerror(-err));
+ return;
+ }
+
+ bt_uhid_register(hogdev->uhid, UHID_OUTPUT, forward_report, hogdev);
+ bt_uhid_register(hogdev->uhid, UHID_FEATURE, forward_report, hogdev);
}
static void info_read_cb(guint8 status, const guint8 *pdu, guint16 plen,
@@ -581,148 +642,6 @@
hogdev);
}
-static void output_written_cb(guint8 status, const guint8 *pdu,
- guint16 plen, gpointer user_data)
-{
- if (status != 0) {
- error("Write output report failed: %s", att_ecode2str(status));
- return;
- }
-}
-
-static int report_type_cmp(gconstpointer a, gconstpointer b)
-{
- const struct report *report = a;
- uint8_t type = GPOINTER_TO_UINT(b);
-
- return report->type - type;
-}
-
-static void forward_report(struct hog_device *hogdev,
- struct uhid_event *ev)
-{
- struct report *report;
- GSList *l;
- void *data;
- int size;
- guint type;
-
- if (hogdev->has_report_id) {
- data = ev->u.output.data + 1;
- size = ev->u.output.size - 1;
- } else {
- data = ev->u.output.data;
- size = ev->u.output.size;
- }
-
- switch (ev->type) {
- case UHID_OUTPUT:
- type = HOG_REPORT_TYPE_OUTPUT;
- break;
- case UHID_FEATURE:
- type = HOG_REPORT_TYPE_FEATURE;
- break;
- default:
- return;
- }
-
- l = g_slist_find_custom(hogdev->reports, GUINT_TO_POINTER(type),
- report_type_cmp);
- if (!l)
- return;
-
- report = l->data;
-
- DBG("Sending report type %d to device 0x%04X handle 0x%X", type,
- hogdev->id, report->decl->value_handle);
-
- if (hogdev->attrib == NULL)
- return;
-
- if (report->decl->properties & GATT_CHR_PROP_WRITE)
- gatt_write_char(hogdev->attrib, report->decl->value_handle,
- data, size, output_written_cb, hogdev);
- else if (report->decl->properties & GATT_CHR_PROP_WRITE_WITHOUT_RESP)
- gatt_write_cmd(hogdev->attrib, report->decl->value_handle,
- data, size, NULL, NULL);
-}
-
-static gboolean uhid_event_cb(GIOChannel *io, GIOCondition cond,
- gpointer user_data)
-{
- struct hog_device *hogdev = user_data;
- struct uhid_event ev;
- ssize_t bread;
- int fd;
-
- if (cond & (G_IO_ERR | G_IO_NVAL))
- goto failed;
-
- fd = g_io_channel_unix_get_fd(io);
- memset(&ev, 0, sizeof(ev));
-
- bread = read(fd, &ev, sizeof(ev));
- if (bread < 0) {
- int err = -errno;
- DBG("uhid-dev read: %s(%d)", strerror(-err), -err);
- goto failed;
- }
-
- DBG("uHID event type %d received", ev.type);
-
- switch (ev.type) {
- case UHID_START:
- case UHID_STOP:
- /* These are called to start and stop the underlying hardware.
- * For HoG we open the channels before creating the device so
- * the hardware is always ready. No need to handle these.
- * Note that these are also called when the kernel switches
- * between device-drivers loaded on the HID device. But we can
- * simply keep the hardware alive during transitions and it
- * works just fine.
- * The kernel never destroys a device itself! Only an explicit
- * UHID_DESTROY request can remove a device. */
- break;
- case UHID_OPEN:
- case UHID_CLOSE:
- /* OPEN/CLOSE are sent whenever user-space opens any interface
- * provided by the kernel HID device. Whenever the open-count
- * is non-zero we must be ready for I/O. As long as it is zero,
- * we can decide to drop all I/O and put the device
- * asleep This is optional, though. Moreover, some
- * special device drivers are buggy in that regard, so
- * maybe we just keep I/O always awake like HIDP in the
- * kernel does. */
- break;
- case UHID_OUTPUT:
- case UHID_FEATURE:
- forward_report(hogdev, &ev);
- break;
- case UHID_OUTPUT_EV:
- /* This is only sent by kernels prior to linux-3.11. It
- * requires us to parse HID-descriptors in user-space to
- * properly handle it. This is redundant as the kernel
- * does it already. That's why newer kernels assemble
- * the output-reports and send it to us via UHID_OUTPUT.
- * We never implemented this, so we rely on users to use
- * recent-enough kernels if they want this feature. No reason
- * to implement this for older kernels. */
- DBG("Unsupported uHID output event: type %d code %d value %d",
- ev.u.output_ev.type, ev.u.output_ev.code,
- ev.u.output_ev.value);
- break;
- default:
- warn("unexpected uHID event");
- break;
- }
-
- return TRUE;
-
-failed:
- hogdev->uhid_watch_id = 0;
- return FALSE;
-}
-
static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
{
struct hog_device *hogdev = user_data;
@@ -807,27 +726,18 @@
struct gatt_primary *prim)
{
struct hog_device *hogdev;
- GIOCondition cond = G_IO_IN | G_IO_ERR | G_IO_NVAL;
- GIOChannel *io;
hogdev = hog_new_device(device, prim->range.start);
if (!hogdev)
return NULL;
- hogdev->uhid_fd = open(UHID_DEVICE_FILE, O_RDWR | O_CLOEXEC);
- if (hogdev->uhid_fd < 0) {
- error("Failed to open uHID device: %s(%d)", strerror(errno),
- errno);
+ hogdev->uhid = bt_uhid_new_default();
+ if (!hogdev->uhid) {
+ error("bt_uhid_new_default: failed");
hog_free_device(hogdev);
return NULL;
}
- io = g_io_channel_unix_new(hogdev->uhid_fd);
- g_io_channel_set_encoding(io, NULL, NULL);
- hogdev->uhid_watch_id = g_io_add_watch(io, cond, uhid_event_cb,
- hogdev);
- g_io_channel_unref(io);
-
hogdev->hog_primary = g_memdup(prim, sizeof(*prim));
hogdev->attioid = btd_device_add_attio_callback(device,
@@ -840,23 +750,8 @@
static int hog_unregister_device(struct hog_device *hogdev)
{
- struct uhid_event ev;
-
btd_device_remove_attio_callback(hogdev->device, hogdev->attioid);
-
- if (hogdev->uhid_watch_id) {
- g_source_remove(hogdev->uhid_watch_id);
- hogdev->uhid_watch_id = 0;
- }
-
- memset(&ev, 0, sizeof(ev));
- ev.type = UHID_DESTROY;
- if (write(hogdev->uhid_fd, &ev, sizeof(ev)) < 0)
- error("Failed to destroy uHID device: %s", strerror(errno));
-
- close(hogdev->uhid_fd);
- hogdev->uhid_fd = -1;
-
+ bt_uhid_unref(hogdev->uhid);
hog_free_device(hogdev);
return 0;
diff --git a/profiles/input/server.c b/profiles/input/server.c
index 772a605..307db96 100644
--- a/profiles/input/server.c
+++ b/profiles/input/server.c
@@ -47,12 +47,17 @@
#include "device.h"
#include "server.h"
+struct confirm_data {
+ bdaddr_t dst;
+ GIOChannel *io;
+};
+
static GSList *servers = NULL;
struct input_server {
bdaddr_t src;
GIOChannel *ctrl;
GIOChannel *intr;
- GIOChannel *confirm;
+ struct confirm_data *confirm;
};
static int server_cmp(gconstpointer s, gconstpointer user_data)
@@ -191,44 +196,36 @@
static void auth_callback(DBusError *derr, void *user_data)
{
struct input_server *server = user_data;
- bdaddr_t src, dst;
+ struct confirm_data *confirm = server->confirm;
GError *err = NULL;
- bt_io_get(server->confirm, &err,
- BT_IO_OPT_SOURCE_BDADDR, &src,
- BT_IO_OPT_DEST_BDADDR, &dst,
- BT_IO_OPT_INVALID);
- if (err) {
- error("%s", err->message);
- g_error_free(err);
- goto reject;
- }
-
if (derr) {
error("Access denied: %s", derr->message);
goto reject;
}
- if (!input_device_exists(&src, &dst) && !dev_is_sixaxis(&src, &dst))
+ if (!input_device_exists(&server->src, &confirm->dst) &&
+ !dev_is_sixaxis(&server->src, &confirm->dst))
return;
- if (!bt_io_accept(server->confirm, connect_event_cb, server,
- NULL, &err)) {
+ if (!bt_io_accept(confirm->io, connect_event_cb, server, NULL, &err)) {
error("bt_io_accept: %s", err->message);
g_error_free(err);
goto reject;
}
- g_io_channel_unref(server->confirm);
+ g_io_channel_unref(confirm->io);
+ g_free(server->confirm);
server->confirm = NULL;
return;
reject:
- g_io_channel_shutdown(server->confirm, TRUE, NULL);
- g_io_channel_unref(server->confirm);
+ g_io_channel_shutdown(confirm->io, TRUE, NULL);
+ g_io_channel_unref(confirm->io);
server->confirm = NULL;
- input_device_close_channels(&src, &dst);
+ input_device_close_channels(&server->src, &confirm->dst);
+ g_free(confirm);
}
static void confirm_event_cb(GIOChannel *chan, gpointer user_data)
@@ -248,7 +245,8 @@
if (err) {
error("%s", err->message);
g_error_free(err);
- goto drop;
+ g_io_channel_shutdown(chan, TRUE, NULL);
+ return;
}
ba2str(&dst, addr);
@@ -263,7 +261,9 @@
goto drop;
}
- server->confirm = g_io_channel_ref(chan);
+ server->confirm = g_new0(struct confirm_data, 1);
+ server->confirm->io = g_io_channel_ref(chan);
+ bacpy(&server->confirm->dst, &dst);
ret = btd_request_authorization(&src, &dst, HID_UUID,
auth_callback, server);
@@ -272,7 +272,8 @@
error("input: authorization for device %s failed", addr);
- g_io_channel_unref(server->confirm);
+ g_io_channel_unref(server->confirm->io);
+ g_free(server->confirm);
server->confirm = NULL;
drop:
diff --git a/profiles/input/uhid_copy.h b/profiles/input/uhid_copy.h
index aff1b3a..9eb11fd 100644
--- a/profiles/input/uhid_copy.h
+++ b/profiles/input/uhid_copy.h
@@ -31,7 +31,7 @@
UHID_OPEN,
UHID_CLOSE,
UHID_OUTPUT,
- UHID_OUTPUT_EV,
+ UHID_OUTPUT_EV, /* obsolete! */
UHID_INPUT,
UHID_FEATURE,
UHID_FEATURE_ANSWER,
@@ -90,6 +90,8 @@
__u8 rtype;
} __attribute__((__packed__));
+/* Obsolete! Newer kernels will no longer send these events but instead convert
+ * it into raw output reports via UHID_OUTPUT. */
struct uhid_output_ev_req {
__u16 type;
__u16 code;
diff --git a/src/device.c b/src/device.c
index 8222610..a9b644b 100644
--- a/src/device.c
+++ b/src/device.c
@@ -1220,6 +1220,9 @@
return;
done:
+ g_slist_free(dev->pending);
+ dev->pending = NULL;
+
if (!dev->connect)
return;
@@ -1241,9 +1244,6 @@
g_dbus_send_reply(dbus_conn, dev->connect, DBUS_TYPE_INVALID);
}
- g_slist_free(dev->pending);
- dev->pending = NULL;
-
dbus_message_unref(dev->connect);
dev->connect = NULL;
}
diff --git a/src/sdp-xml.c b/src/sdp-xml.c
index 6492781..0535d09 100644
--- a/src/sdp-xml.c
+++ b/src/sdp-xml.c
@@ -91,6 +91,10 @@
/* Null terminate the text */
elem->size = DEFAULT_XML_DATA_SIZE;
elem->text = malloc(DEFAULT_XML_DATA_SIZE);
+ if (!elem->text) {
+ free(elem);
+ return NULL;
+ }
elem->text[0] = '\0';
return elem;
@@ -333,6 +337,8 @@
int i;
decoded = malloc((len >> 1) + 1);
+ if (!decoded)
+ return NULL;
/* Ensure the string is a power of 2 */
len = (len >> 1) << 1;
@@ -363,6 +369,8 @@
url = sdp_xml_parse_string_decode(data,
SDP_XML_ENCODING_NORMAL, &length);
+ if (!url)
+ return NULL;
if (length > UCHAR_MAX)
dtd = SDP_URL_STR16;
@@ -382,6 +390,8 @@
sdp_data_t *ret;
text = sdp_xml_parse_string_decode(data, encoding, &length);
+ if (!text)
+ return NULL;
if (length > UCHAR_MAX)
dtd = SDP_TEXT_STR16;
@@ -823,7 +833,7 @@
{
int num_chars_to_escape = 0;
int length = value->unitSize - 1;
- char *strBuf = 0;
+ char *strBuf;
hex = 0;
@@ -850,6 +860,10 @@
appender(data, "encoding=\"hex\" ");
strBuf = malloc(sizeof(char)
* ((value->unitSize-1) * 2 + 1));
+ if (!strBuf) {
+ DBG("No memory to convert raw data to xml");
+ return;
+ }
/* Unit Size seems to include the size for dtd
It is thus off by 1
@@ -866,6 +880,10 @@
/* escape the XML disallowed chars */
strBuf = malloc(sizeof(char) *
(value->unitSize + 1 + num_chars_to_escape * 4));
+ if (!strBuf) {
+ DBG("No memory to convert raw data to xml");
+ return;
+ }
for (i = 0, j = 0; i < length; i++) {
if (value->val.str[i] == '&') {
strBuf[j++] = '&';
diff --git a/src/sdpd-database.c b/src/sdpd-database.c
index f65a526..6f5577b 100644
--- a/src/sdpd-database.c
+++ b/src/sdpd-database.c
@@ -157,6 +157,12 @@
void sdp_svcdb_set_collectable(sdp_record_t *record, int sock)
{
sdp_indexed_t *item = malloc(sizeof(sdp_indexed_t));
+
+ if (!item) {
+ SDPDBG("No memory");
+ return;
+ }
+
item->sock = sock;
item->record = record;
socket_index = sdp_list_insert_sorted(socket_index, item, compare_indices);
diff --git a/src/shared/crypto.c b/src/shared/crypto.c
index 9e94122..e2b0e7a 100644
--- a/src/shared/crypto.c
+++ b/src/shared/crypto.c
@@ -74,6 +74,7 @@
int ref_count;
int ecb_aes;
int urandom;
+ int cmac_aes;
};
static int urandom_setup(void)
@@ -109,6 +110,28 @@
return fd;
}
+static int cmac_aes_setup(void)
+{
+ struct sockaddr_alg salg;
+ int fd;
+
+ 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;
+ }
+
+ return fd;
+}
+
struct bt_crypto *bt_crypto_new(void)
{
struct bt_crypto *crypto;
@@ -130,6 +153,14 @@
return NULL;
}
+ crypto->cmac_aes = cmac_aes_setup();
+ if (crypto->cmac_aes < 0) {
+ close(crypto->urandom);
+ close(crypto->ecb_aes);
+ free(crypto);
+ return NULL;
+ }
+
return bt_crypto_ref(crypto);
}
@@ -153,6 +184,7 @@
close(crypto->urandom);
close(crypto->ecb_aes);
+ close(crypto->cmac_aes);
free(crypto);
}
@@ -220,14 +252,74 @@
return true;
}
-static inline void swap128(const uint8_t src[16], uint8_t dst[16])
+static inline void swap_buf(const uint8_t *src, uint8_t *dst, uint16_t len)
{
int i;
- for (i = 0; i < 16; i++)
- dst[15 - i] = src[i];
+ for (i = 0; i < len; i++)
+ dst[len - 1 - i] = src[i];
}
+bool bt_crypto_sign_att(struct bt_crypto *crypto, const uint8_t key[16],
+ const uint8_t *m, uint16_t m_len,
+ uint32_t sign_cnt, uint8_t signature[12])
+{
+ int fd;
+ int len;
+ uint8_t tmp[16], out[16];
+ uint16_t msg_len = m_len + sizeof(uint32_t);
+ uint8_t msg[msg_len];
+ uint8_t msg_s[msg_len];
+
+ if (!crypto)
+ return false;
+
+ memset(msg, 0, msg_len);
+ memcpy(msg, m, m_len);
+
+ /* Add sign_counter to the message */
+ put_le32(sign_cnt, msg + m_len);
+
+ /* The most significant octet of key corresponds to key[0] */
+ swap_buf(key, tmp, 16);
+
+ fd = alg_new(crypto->cmac_aes, tmp, 16);
+ if (fd < 0)
+ return false;
+
+ /* Swap msg before signing */
+ swap_buf(msg, msg_s, msg_len);
+
+ len = send(fd, msg_s, msg_len, 0);
+ if (len < 0) {
+ close(fd);
+ return false;
+ }
+
+ len = read(fd, out, 16);
+ if (len < 0) {
+ close(fd);
+ return false;
+ }
+
+ close(fd);
+
+ /*
+ * As to BT spec. 4.1 Vol[3], Part C, chapter 10.4.1 sign counter should
+ * be placed in the signature
+ */
+ put_be32(sign_cnt, out + 8);
+
+ /*
+ * The most significant octet of hash corresponds to out[0] - swap it.
+ * Then truncate in most significant bit first order to a length of
+ * 12 octets
+ */
+ swap_buf(out, tmp, 16);
+ memcpy(signature, tmp + 4, 12);
+
+ return true;
+}
/*
* Security function e
*
@@ -251,7 +343,7 @@
return false;
/* The most significant octet of key corresponds to key[0] */
- swap128(key, tmp);
+ swap_buf(key, tmp, 16);
fd = alg_new(crypto->ecb_aes, tmp, 16);
if (fd < 0)
@@ -259,7 +351,7 @@
/* Most significant octet of plaintextData corresponds to in[0] */
- swap128(plaintext, in);
+ swap_buf(plaintext, in, 16);
if (!alg_encrypt(fd, in, 16, out, 16)) {
close(fd);
@@ -267,7 +359,7 @@
}
/* Most significant octet of encryptedData corresponds to out[0] */
- swap128(out, encrypted);
+ swap_buf(out, encrypted, 16);
close(fd);
diff --git a/src/shared/crypto.h b/src/shared/crypto.h
index cae8daa..b10f3ff 100644
--- a/src/shared/crypto.h
+++ b/src/shared/crypto.h
@@ -46,3 +46,6 @@
bool bt_crypto_s1(struct bt_crypto *crypto, const uint8_t k[16],
const uint8_t r1[16], const uint8_t r2[16],
uint8_t res[16]);
+bool bt_crypto_sign_att(struct bt_crypto *crypto, const uint8_t key[16],
+ const uint8_t *m, uint16_t m_len,
+ uint32_t sign_cnt, uint8_t signature[12]);
diff --git a/src/shared/gatt-db.c b/src/shared/gatt-db.c
index 36316af..9cbf409 100644
--- a/src/shared/gatt-db.c
+++ b/src/shared/gatt-db.c
@@ -48,7 +48,7 @@
struct gatt_db_attribute {
uint16_t handle;
bt_uuid_t uuid;
- uint8_t permissions;
+ uint32_t permissions;
uint16_t value_len;
uint8_t *value;
@@ -95,10 +95,8 @@
return attribute;
}
-static void attribute_destroy(void *data)
+static void attribute_destroy(struct gatt_db_attribute *attribute)
{
- struct gatt_db_attribute *attribute = data;
-
/* Attribute was not initialized by user */
if (!attribute)
return;
@@ -258,7 +256,7 @@
static void set_attribute_data(struct gatt_db_attribute *attribute,
gatt_db_read_t read_func,
gatt_db_write_t write_func,
- uint8_t permissions,
+ uint32_t permissions,
void *user_data)
{
attribute->permissions = permissions;
@@ -269,7 +267,7 @@
uint16_t gatt_db_add_characteristic(struct gatt_db *db, uint16_t handle,
const bt_uuid_t *uuid,
- uint8_t permissions,
+ uint32_t permissions,
uint8_t properties,
gatt_db_read_t read_func,
gatt_db_write_t write_func,
@@ -317,7 +315,7 @@
uint16_t gatt_db_add_char_descriptor(struct gatt_db *db, uint16_t handle,
const bt_uuid_t *uuid,
- uint8_t permissions,
+ uint32_t permissions,
gatt_db_read_t read_func,
gatt_db_write_t write_func,
void *user_data)
@@ -733,3 +731,31 @@
return service->attributes[0]->handle + service->num_handles - 1;
}
+
+bool gatt_db_get_attribute_permissions(struct gatt_db *db, uint16_t handle,
+ uint32_t *permissions)
+{
+ struct gatt_db_attribute *attribute;
+ struct gatt_db_service *service;
+ uint16_t service_handle;
+
+ service = queue_find(db->services, find_service_for_handle,
+ INT_TO_PTR(handle));
+ if (!service)
+ return false;
+
+ service_handle = service->attributes[0]->handle;
+
+ /*
+ * We can safely get attribute from attributes array with offset,
+ * because find_service_for_handle() check if given handle is
+ * in service range.
+ */
+ attribute = service->attributes[handle - service_handle];
+ if (!attribute)
+ return false;
+
+ *permissions = attribute->permissions;
+ return true;
+
+}
diff --git a/src/shared/gatt-db.h b/src/shared/gatt-db.h
index 3d46730..a88f637 100644
--- a/src/shared/gatt-db.h
+++ b/src/shared/gatt-db.h
@@ -41,7 +41,7 @@
uint16_t gatt_db_add_characteristic(struct gatt_db *db, uint16_t handle,
const bt_uuid_t *uuid,
- uint8_t permissions,
+ uint32_t permissions,
uint8_t properties,
gatt_db_read_t read_func,
gatt_db_write_t write_func,
@@ -49,7 +49,7 @@
uint16_t gatt_db_add_char_descriptor(struct gatt_db *db, uint16_t handle,
const bt_uuid_t *uuid,
- uint8_t permissions,
+ uint32_t permissions,
gatt_db_read_t read_func,
gatt_db_write_t write_func,
void *user_data);
@@ -91,3 +91,6 @@
uint16_t handle);
uint16_t gatt_db_get_end_handle(struct gatt_db *db, uint16_t handle);
+
+bool gatt_db_get_attribute_permissions(struct gatt_db *db, uint16_t handle,
+ uint32_t *permissions);
diff --git a/src/shared/hfp.c b/src/shared/hfp.c
index 36c8c3e..196e777 100644
--- a/src/shared/hfp.c
+++ b/src/shared/hfp.c
@@ -381,6 +381,7 @@
{
char *str, *ptr;
size_t len, count;
+ bool free_ptr = false;
str = ringbuf_peek(hfp->read_buf, 0, &len);
if (!str)
@@ -407,6 +408,7 @@
*ptr = '\0';
count = asprintf(&ptr, "%s%s", str, str2);
+ free_ptr = true;
str = ptr;
} else {
count = ptr - str;
@@ -424,7 +426,7 @@
len = ringbuf_drain(hfp->read_buf, count + 1);
- if (str == ptr)
+ if (free_ptr)
free(ptr);
}
diff --git a/src/shared/queue.c b/src/shared/queue.c
index 8a69729..8bbb7df 100644
--- a/src/shared/queue.c
+++ b/src/shared/queue.c
@@ -34,11 +34,30 @@
};
struct queue {
+ int ref_count;
struct queue_entry *head;
struct queue_entry *tail;
unsigned int entries;
};
+static struct queue *queue_ref(struct queue *queue)
+{
+ if (!queue)
+ return NULL;
+
+ __sync_fetch_and_add(&queue->ref_count, 1);
+
+ return queue;
+}
+
+static void queue_unref(struct queue *queue)
+{
+ if (__sync_sub_and_fetch(&queue->ref_count, 1))
+ return;
+
+ free(queue);
+}
+
struct queue *queue_new(void)
{
struct queue *queue;
@@ -51,7 +70,7 @@
queue->tail = NULL;
queue->entries = 0;
- return queue;
+ return queue_ref(queue);
}
void queue_destroy(struct queue *queue, queue_destroy_func_t destroy)
@@ -74,7 +93,7 @@
free(tmp);
}
- free(queue);
+ queue_unref(queue);
}
bool queue_push_tail(struct queue *queue, void *data)
@@ -168,6 +187,17 @@
return queue->tail->data;
}
+static bool queue_find_entry(struct queue *queue, const void *data)
+{
+ struct queue_entry *entry;
+
+ for (entry = queue->head; entry; entry = entry->next)
+ if (entry == data)
+ return true;
+
+ return false;
+}
+
void queue_foreach(struct queue *queue, queue_foreach_func_t function,
void *user_data)
{
@@ -177,26 +207,41 @@
return;
entry = queue->head;
+ if (!entry)
+ return;
- while (entry) {
+ queue_ref(queue);
+ while (entry && queue->ref_count > 1) {
struct queue_entry *tmp = entry;
entry = tmp->next;
function(tmp->data, user_data);
+
+ if (!queue_find_entry(queue, entry))
+ break;
}
+ queue_unref(queue);
+}
+
+static bool direct_match(const void *a, const void *b)
+{
+ return a == b;
}
void *queue_find(struct queue *queue, queue_match_func_t function,
- void *user_data)
+ const void *match_data)
{
struct queue_entry *entry;
if (!queue || !function)
return NULL;
+ if (!function)
+ function = direct_match;
+
for (entry = queue->head; entry; entry = entry->next)
- if (function(entry->data, user_data))
+ if (function(entry->data, match_data))
return entry->data;
return NULL;
diff --git a/src/shared/queue.h b/src/shared/queue.h
index 8201ff8..709590b 100644
--- a/src/shared/queue.h
+++ b/src/shared/queue.h
@@ -44,7 +44,7 @@
typedef bool (*queue_match_func_t)(const void *a, const void *b);
void *queue_find(struct queue *queue, queue_match_func_t function,
- void *user_data);
+ const void *match_data);
bool queue_remove(struct queue *queue, void *data);
void *queue_remove_if(struct queue *queue, queue_match_func_t function,
diff --git a/src/shared/uhid.c b/src/shared/uhid.c
new file mode 100644
index 0000000..28c1792
--- /dev/null
+++ b/src/shared/uhid.c
@@ -0,0 +1,245 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2014 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 <string.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include "src/shared/io.h"
+#include "src/shared/util.h"
+#include "src/shared/queue.h"
+#include "src/shared/uhid.h"
+
+#define UHID_DEVICE_FILE "/dev/uhid"
+
+struct bt_uhid {
+ int ref_count;
+ struct io *io;
+ unsigned int notify_id;
+ struct queue *notify_list;
+};
+
+struct uhid_notify {
+ unsigned int id;
+ uint32_t event;
+ bt_uhid_callback_t func;
+ void *user_data;
+};
+
+static void uhid_free(struct bt_uhid *uhid)
+{
+ if (uhid->io)
+ io_destroy(uhid->io);
+
+ if (uhid->notify_list)
+ queue_destroy(uhid->notify_list, free);
+
+ free(uhid);
+}
+
+static void notify_handler(void *data, void *user_data)
+{
+ struct uhid_notify *notify = data;
+ struct uhid_event *ev = user_data;
+
+ if (notify->event != ev->type)
+ return;
+
+ if (notify->func)
+ notify->func(ev, notify->user_data);
+}
+
+static bool uhid_read_handler(struct io *io, void *user_data)
+{
+ struct bt_uhid *uhid = user_data;
+ int fd;
+ ssize_t len;
+ struct uhid_event ev;
+
+ fd = io_get_fd(io);
+ if (fd < 0)
+ return false;
+
+ memset(&ev, 0, sizeof(ev));
+
+ len = read(fd, &ev, sizeof(ev));
+ if (len < 0)
+ return false;
+
+ if ((size_t) len < sizeof(ev.type))
+ return false;
+
+ queue_foreach(uhid->notify_list, notify_handler, &ev);
+
+ return true;
+}
+
+struct bt_uhid *bt_uhid_new_default(void)
+{
+ struct bt_uhid *uhid;
+ int fd;
+
+ fd = open(UHID_DEVICE_FILE, O_RDWR | O_CLOEXEC);
+ if (fd < 0)
+ return NULL;
+
+ uhid = bt_uhid_new(fd);
+ if (!uhid) {
+ close(fd);
+ return NULL;
+ }
+
+ io_set_close_on_destroy(uhid->io, true);
+
+ return uhid;
+}
+
+struct bt_uhid *bt_uhid_new(int fd)
+{
+ struct bt_uhid *uhid;
+
+ uhid = new0(struct bt_uhid, 1);
+ if (!uhid)
+ return NULL;
+
+ uhid->io = io_new(fd);
+ if (!uhid->io)
+ goto failed;
+
+ uhid->notify_list = queue_new();
+ if (!uhid->notify_list)
+ goto failed;
+
+ if (!io_set_read_handler(uhid->io, uhid_read_handler, uhid, NULL))
+ goto failed;
+
+ return bt_uhid_ref(uhid);
+
+failed:
+ uhid_free(uhid);
+ return NULL;
+}
+
+struct bt_uhid *bt_uhid_ref(struct bt_uhid *uhid)
+{
+ if (!uhid)
+ return NULL;
+
+ __sync_fetch_and_add(&uhid->ref_count, 1);
+
+ return uhid;
+}
+
+void bt_uhid_unref(struct bt_uhid *uhid)
+{
+ if (!uhid)
+ return;
+
+ if (__sync_sub_and_fetch(&uhid->ref_count, 1))
+ return;
+
+ uhid_free(uhid);
+}
+
+bool bt_uhid_set_close_on_unref(struct bt_uhid *uhid, bool do_close)
+{
+ if (!uhid || !uhid->io)
+ return false;
+
+ io_set_close_on_destroy(uhid->io, do_close);
+
+ return true;
+}
+
+unsigned int bt_uhid_register(struct bt_uhid *uhid, uint32_t event,
+ bt_uhid_callback_t func, void *user_data)
+{
+ struct uhid_notify *notify;
+
+ if (!uhid)
+ return 0;
+
+ notify = new0(struct uhid_notify, 1);
+ if (!notify)
+ return 0;
+
+ notify->id = uhid->notify_id++;
+ notify->event = event;
+ notify->func = func;
+ notify->user_data = user_data;
+
+ if (!queue_push_tail(uhid->notify_list, notify)) {
+ free(notify);
+ return 0;
+ }
+
+ return notify->id;
+}
+
+static bool match_notify_id(const void *a, const void *b)
+{
+ const struct uhid_notify *notify = a;
+ unsigned int id = PTR_TO_UINT(b);
+
+ return notify->id == id;
+}
+
+bool bt_uhid_unregister(struct bt_uhid *uhid, unsigned int id)
+{
+ struct uhid_notify *notify;
+
+ if (!uhid || !id)
+ return false;
+
+ notify = queue_remove_if(uhid->notify_list, match_notify_id,
+ UINT_TO_PTR(id));
+ if (!notify)
+ return false;
+
+ free(notify);
+ return true;
+}
+
+int bt_uhid_send(struct bt_uhid *uhid, const struct uhid_event *ev)
+{
+ int fd;
+ ssize_t len;
+
+ if (!uhid->io)
+ return -ENOTCONN;
+
+ fd = io_get_fd(uhid->io);
+
+ len = write(fd, ev, sizeof(*ev));
+ if (len < 0)
+ return -errno;
+
+ /* uHID kernel driver does not handle partial writes */
+ return len != sizeof(*ev) ? -EIO : 0;
+}
diff --git a/src/shared/uhid.h b/src/shared/uhid.h
new file mode 100644
index 0000000..459a249
--- /dev/null
+++ b/src/shared/uhid.h
@@ -0,0 +1,44 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2014 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>
+
+#include "profiles/input/uhid_copy.h"
+
+struct bt_uhid;
+
+struct bt_uhid *bt_uhid_new_default(void);
+struct bt_uhid *bt_uhid_new(int fd);
+
+struct bt_uhid *bt_uhid_ref(struct bt_uhid *uhid);
+void bt_uhid_unref(struct bt_uhid *uhid);
+
+bool bt_uhid_set_close_on_unref(struct bt_uhid *uhid, bool do_close);
+
+typedef void (*bt_uhid_callback_t)(struct uhid_event *ev, void *user_data);
+unsigned int bt_uhid_register(struct bt_uhid *uhid, uint32_t event,
+ bt_uhid_callback_t func, void *user_data);
+bool bt_uhid_unregister(struct bt_uhid *uhid, unsigned int id);
+
+int bt_uhid_send(struct bt_uhid *uhid, const struct uhid_event *ev);
diff --git a/tools/avinfo.c b/tools/avinfo.c
index a4deaac..7d58e25 100644
--- a/tools/avinfo.c
+++ b/tools/avinfo.c
@@ -158,21 +158,95 @@
uint8_t caps[0];
} __attribute__ ((packed));
-static void print_vendor(a2dp_vendor_codec_t *vendor)
+static void print_aptx(a2dp_aptx_t *aptx)
{
- printf("\tMedia Codec: Vendor Specific A2DP Codec");
+ printf("\t\tVendor Specific Value (aptX)");
- printf("\n\t\tVendor ID 0x%02x%02x%02x%02x", vendor->vendor_id[0],
- vendor->vendor_id[1], vendor->vendor_id[2],
- vendor->vendor_id[3]);
+ printf("\n\t\t\tFrequencies: ");
+ if (aptx->frequency & APTX_SAMPLING_FREQ_16000)
+ printf("16kHz ");
+ if (aptx->frequency & APTX_SAMPLING_FREQ_32000)
+ printf("32kHz ");
+ if (aptx->frequency & APTX_SAMPLING_FREQ_44100)
+ printf("44.1kHz ");
+ if (aptx->frequency & APTX_SAMPLING_FREQ_48000)
+ printf("48kHz ");
- printf("\n\t\tVendor Specific Codec ID 0x%02x%02x\n",
- vendor->codec_id[0], vendor->codec_id[1]);
+ printf("\n\t\t\tChannel modes: ");
+ if (aptx->channel_mode & APTX_CHANNEL_MODE_MONO)
+ printf("Mono ");
+ if (aptx->channel_mode & APTX_CHANNEL_MODE_STEREO)
+ printf("Stereo ");
+
+ printf("\n");
}
-static void print_mpeg24(a2dp_mpeg_t *mpeg)
+static void print_vendor(a2dp_vendor_codec_t *vendor)
{
- printf("\tMedia Codec: MPEG24\n");
+ uint32_t vendor_id = btohl(vendor->vendor_id);
+ uint16_t codec_id = btohs(vendor->codec_id);
+
+ printf("\tMedia Codec: Vendor Specific A2DP Codec");
+
+ printf("\n\t\tVendor ID 0x%08x", vendor_id);
+
+ printf("\n\t\tVendor Specific Codec ID 0x%04x\n", codec_id);
+
+ if (vendor_id == APTX_VENDOR_ID && codec_id == APTX_CODEC_ID)
+ print_aptx((void *) vendor);
+}
+
+static void print_mpeg24(a2dp_aac_t *aac)
+{
+ unsigned freq = AAC_GET_FREQUENCY(*aac);
+ unsigned bitrate = AAC_GET_BITRATE(*aac);
+
+ printf("\tMedia Codec: MPEG24\n\t\tObject Types: ");
+
+ if (aac->object_type & AAC_OBJECT_TYPE_MPEG2_AAC_LC)
+ printf("MPEG-2 AAC LC ");
+ if (aac->object_type & AAC_OBJECT_TYPE_MPEG4_AAC_LC)
+ printf("MPEG-4 AAC LC ");
+ if (aac->object_type & AAC_OBJECT_TYPE_MPEG4_AAC_LTP)
+ printf("MPEG-4 AAC LTP ");
+ if (aac->object_type & AAC_OBJECT_TYPE_MPEG4_AAC_SCA)
+ printf("MPEG-4 AAC scalable ");
+
+ printf("\n\t\tFrequencies: ");
+ if (freq & AAC_SAMPLING_FREQ_8000)
+ printf("8kHz ");
+ if (freq & AAC_SAMPLING_FREQ_11025)
+ printf("11.025kHz ");
+ if (freq & AAC_SAMPLING_FREQ_12000)
+ printf("12kHz ");
+ if (freq & AAC_SAMPLING_FREQ_16000)
+ printf("16kHz ");
+ if (freq & AAC_SAMPLING_FREQ_22050)
+ printf("22.05kHz ");
+ if (freq & AAC_SAMPLING_FREQ_24000)
+ printf("24kHz ");
+ if (freq & AAC_SAMPLING_FREQ_32000)
+ printf("32kHz ");
+ if (freq & AAC_SAMPLING_FREQ_44100)
+ printf("44.1kHz ");
+ if (freq & AAC_SAMPLING_FREQ_48000)
+ printf("48kHz ");
+ if (freq & AAC_SAMPLING_FREQ_64000)
+ printf("64kHz ");
+ if (freq & AAC_SAMPLING_FREQ_88200)
+ printf("88.2kHz ");
+ if (freq & AAC_SAMPLING_FREQ_96000)
+ printf("96kHz ");
+
+ printf("\n\t\tChannels: ");
+ if (aac->channels & AAC_CHANNELS_1)
+ printf("1 ");
+ if (aac->channels & AAC_CHANNELS_2)
+ printf("2 ");
+
+ printf("\n\t\tBitrate: %u", bitrate);
+
+ printf("\n\t\tVBR: %s", aac->vbr ? "Yes\n" : "No\n");
}
static void print_mpeg12(a2dp_mpeg_t *mpeg)
diff --git a/tools/btmgmt.c b/tools/btmgmt.c
index 0618de1..8f5a981 100644
--- a/tools/btmgmt.c
+++ b/tools/btmgmt.c
@@ -538,12 +538,12 @@
}
static int mgmt_confirm_reply(struct mgmt *mgmt, uint16_t index,
- const bdaddr_t *bdaddr)
+ const struct mgmt_addr_info *addr)
{
struct mgmt_cp_user_confirm_reply cp;
memset(&cp, 0, sizeof(cp));
- bacpy(&cp.addr.bdaddr, bdaddr);
+ memcpy(&cp.addr, addr, sizeof(*addr));
return mgmt_reply(mgmt, MGMT_OP_USER_CONFIRM_REPLY, index,
sizeof(cp), &cp, confirm_rsp, NULL, NULL);
@@ -564,12 +564,12 @@
}
static int mgmt_confirm_neg_reply(struct mgmt *mgmt, uint16_t index,
- const bdaddr_t *bdaddr)
+ const struct mgmt_addr_info *addr)
{
struct mgmt_cp_user_confirm_reply cp;
memset(&cp, 0, sizeof(cp));
- bacpy(&cp.addr.bdaddr, bdaddr);
+ memcpy(&cp.addr, addr, sizeof(*addr));
return mgmt_reply(mgmt, MGMT_OP_USER_CONFIRM_NEG_REPLY, index,
sizeof(cp), &cp, confirm_neg_rsp, NULL, NULL);
@@ -609,7 +609,7 @@
memset(rsp, 0, sizeof(rsp));
if (fgets(rsp, sizeof(rsp), stdin) == NULL || rsp[0] == '\n') {
- mgmt_confirm_neg_reply(mgmt, index, &ev->addr.bdaddr);
+ mgmt_confirm_neg_reply(mgmt, index, &ev->addr);
return;
}
@@ -618,9 +618,124 @@
rsp[rsp_len - 1] = '\0';
if (rsp[0] == 'y' || rsp[0] == 'Y')
- mgmt_confirm_reply(mgmt, index, &ev->addr.bdaddr);
+ mgmt_confirm_reply(mgmt, index, &ev->addr);
else
- mgmt_confirm_neg_reply(mgmt, index, &ev->addr.bdaddr);
+ mgmt_confirm_neg_reply(mgmt, index, &ev->addr);
+}
+
+static void passkey_rsp(uint8_t status, uint16_t len, const void *param,
+ void *user_data)
+{
+ if (status != 0) {
+ fprintf(stderr,
+ "User Passkey reply failed. status 0x%02x (%s)\n",
+ status, mgmt_errstr(status));
+ mainloop_quit();
+ return;
+ }
+
+ printf("User Passkey Reply successful\n");
+}
+
+static int mgmt_passkey_reply(struct mgmt *mgmt, uint16_t index,
+ const struct mgmt_addr_info *addr,
+ uint32_t passkey)
+{
+ struct mgmt_cp_user_passkey_reply cp;
+
+ memset(&cp, 0, sizeof(cp));
+ memcpy(&cp.addr, addr, sizeof(*addr));
+ put_le32(passkey, &cp.passkey);
+
+ return mgmt_reply(mgmt, MGMT_OP_USER_PASSKEY_REPLY, index,
+ sizeof(cp), &cp, passkey_rsp, NULL, NULL);
+}
+
+static void passkey_neg_rsp(uint8_t status, uint16_t len, const void *param,
+ void *user_data)
+{
+ if (status != 0) {
+ fprintf(stderr,
+ "Passkey Neg reply failed. status 0x%02x (%s)\n",
+ status, mgmt_errstr(status));
+ mainloop_quit();
+ return;
+ }
+
+ printf("User Passkey Negative Reply successful\n");
+}
+
+static int mgmt_passkey_neg_reply(struct mgmt *mgmt, uint16_t index,
+ const struct mgmt_addr_info *addr)
+{
+ struct mgmt_cp_user_passkey_reply cp;
+
+ memset(&cp, 0, sizeof(cp));
+ memcpy(&cp.addr, addr, sizeof(*addr));
+
+ return mgmt_reply(mgmt, MGMT_OP_USER_PASSKEY_NEG_REPLY, index,
+ sizeof(cp), &cp, passkey_neg_rsp, NULL, NULL);
+}
+
+
+static void request_passkey(uint16_t index, uint16_t len, const void *param,
+ void *user_data)
+{
+ const struct mgmt_ev_user_passkey_request *ev = param;
+ struct mgmt *mgmt = user_data;
+ char passkey[7];
+
+ if (len != sizeof(*ev)) {
+ fprintf(stderr,
+ "Invalid passkey request length (%u bytes)\n", len);
+ return;
+ }
+
+ if (monitor) {
+ char addr[18];
+ ba2str(&ev->addr.bdaddr, addr);
+ printf("hci%u %s request passkey\n", index, addr);
+ }
+
+ printf("Passkey Request (press enter to reject) >> ");
+ fflush(stdout);
+
+ memset(passkey, 0, sizeof(passkey));
+
+ if (fgets(passkey, sizeof(passkey), stdin) == NULL ||
+ passkey[0] == '\n') {
+ mgmt_passkey_neg_reply(mgmt, index, &ev->addr);
+ return;
+ }
+
+ len = strlen(passkey);
+ if (passkey[len - 1] == '\n') {
+ passkey[len - 1] = '\0';
+ len--;
+ }
+
+ mgmt_passkey_reply(mgmt, index, &ev->addr, atoi(passkey));
+}
+
+static void passkey_notify(uint16_t index, uint16_t len, const void *param,
+ void *user_data)
+{
+ const struct mgmt_ev_passkey_notify *ev = param;
+
+ if (len != sizeof(*ev)) {
+ fprintf(stderr,
+ "Invalid passkey request length (%u bytes)\n", len);
+ return;
+ }
+
+ if (monitor) {
+ char addr[18];
+ ba2str(&ev->addr.bdaddr, addr);
+ printf("hci%u %s request passkey\n", index, addr);
+ }
+
+ printf("Passkey Notify: %06u (entered %u)\n", get_le32(&ev->passkey),
+ ev->entered);
}
static void cmd_monitor(struct mgmt *mgmt, uint16_t index, int argc,
@@ -2225,6 +2340,89 @@
}
}
+static void io_cap_rsp(uint8_t status, uint16_t len, const void *param,
+ void *user_data)
+{
+ if (status != 0)
+ fprintf(stderr, "Could not set IO Capability with "
+ "status 0x%02x (%s)\n",
+ status, mgmt_errstr(status));
+ else
+ printf("IO Capabilities successfully set\n");
+
+ mainloop_quit();
+}
+
+static void io_cap_usage(void)
+{
+ printf("Usage: btmgmt io-cap <cap>\n");
+}
+
+static void cmd_io_cap(struct mgmt *mgmt, uint16_t index,
+ int argc, char **argv)
+{
+ struct mgmt_cp_set_io_capability cp;
+ uint8_t cap;
+
+ if (argc < 2) {
+ io_cap_usage();
+ exit(EXIT_FAILURE);
+ }
+
+ if (index == MGMT_INDEX_NONE)
+ index = 0;
+
+ cap = strtol(argv[1], NULL, 0);
+ memset(&cp, 0, sizeof(cp));
+ cp.io_capability = cap;
+
+ if (mgmt_send(mgmt, MGMT_OP_SET_IO_CAPABILITY, index, sizeof(cp), &cp,
+ io_cap_rsp, NULL, NULL) == 0) {
+ fprintf(stderr, "Unable to send set-io-cap cmd\n");
+ exit(EXIT_FAILURE);
+ }
+}
+
+static void scan_params_rsp(uint8_t status, uint16_t len, const void *param,
+ void *user_data)
+{
+ if (status != 0)
+ fprintf(stderr, "Set scan parameters failed with status 0x%02x (%s)\n",
+ status, mgmt_errstr(status));
+ else
+ printf("Scan parameters successfully set\n");
+
+ mainloop_quit();
+}
+
+static void scan_params_usage(void)
+{
+ printf("Usage: btmgmt scan-params <interval> <window>\n");
+}
+
+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();
+ exit(EXIT_FAILURE);
+ }
+
+ if (index == MGMT_INDEX_NONE)
+ index = 0;
+
+ cp.interval = strtol(argv[1], NULL, 0);
+ cp.window = strtol(argv[2], NULL, 0);
+
+ if (mgmt_send(mgmt, MGMT_OP_SET_SCAN_PARAMS, index, sizeof(cp), &cp,
+ scan_params_rsp, NULL, NULL) == 0) {
+ fprintf(stderr, "Unable to send set_scan_params cmd\n");
+ exit(EXIT_FAILURE);
+ }
+}
+
static struct {
char *cmd;
void (*func)(struct mgmt *mgmt, uint16_t index, int argc, char **argv);
@@ -2245,7 +2443,7 @@
{ "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", },
+ { "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" },
@@ -2268,6 +2466,8 @@
{ "static-addr",cmd_static_addr,"Set static address" },
{ "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" },
{ }
};
@@ -2386,6 +2586,10 @@
mgmt, NULL);
mgmt_register(mgmt, MGMT_EV_USER_CONFIRM_REQUEST, index, user_confirm,
mgmt, NULL);
+ mgmt_register(mgmt, MGMT_EV_USER_PASSKEY_REQUEST, index,
+ request_passkey, mgmt, NULL);
+ mgmt_register(mgmt, MGMT_EV_PASSKEY_NOTIFY, index,
+ passkey_notify, mgmt, NULL);
exit_status = mainloop_run();
diff --git a/tools/l2test.c b/tools/l2test.c
index c70bac0..ffad7c4 100644
--- a/tools/l2test.c
+++ b/tools/l2test.c
@@ -271,7 +271,7 @@
memset(opts, 0, sizeof(*opts));
- if (bdaddr_type == BDADDR_BREDR || cid) {
+ if (bdaddr_type == BDADDR_BREDR) {
optlen = sizeof(*opts);
return getsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, opts, &optlen);
}
@@ -287,7 +287,7 @@
static int setopts(int sk, struct l2cap_options *opts)
{
- if (bdaddr_type == BDADDR_BREDR || cid)
+ if (bdaddr_type == BDADDR_BREDR)
return setsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, opts,
sizeof(*opts));
diff --git a/tools/mgmt-tester.c b/tools/mgmt-tester.c
index 4cc7ba4..c99fe18 100644
--- a/tools/mgmt-tester.c
+++ b/tools/mgmt-tester.c
@@ -395,6 +395,7 @@
uint8_t client_auth_req;
bool reject_ssp;
bool client_reject_ssp;
+ bool just_works;
};
static const char dummy_data[] = { 0x00 };
@@ -2212,6 +2213,15 @@
.expect_status = MGMT_STATUS_INVALID_PARAMS,
};
+static const char set_io_cap_invalid_param_1[] = { 0xff };
+
+static const struct generic_data set_io_cap_invalid_param_test_1 = {
+ .send_opcode = MGMT_OP_SET_IO_CAPABILITY,
+ .send_param = set_io_cap_invalid_param_1,
+ .send_len = sizeof(set_io_cap_invalid_param_1),
+ .expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
static const char pair_device_param[] = {
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x00, 0x00 };
static const char pair_device_rsp[] = {
@@ -2220,6 +2230,10 @@
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0xff, 0x00 };
static const char pair_device_invalid_param_rsp_1[] = {
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0xff };
+static const char pair_device_invalid_param_2[] = {
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x00, 0x05 };
+static const char pair_device_invalid_param_rsp_2[] = {
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x00 };
static const struct generic_data pair_device_not_powered_test_1 = {
.send_opcode = MGMT_OP_PAIR_DEVICE,
@@ -2239,6 +2253,15 @@
.expect_len = sizeof(pair_device_invalid_param_rsp_1),
};
+static const struct generic_data pair_device_invalid_param_test_2 = {
+ .send_opcode = MGMT_OP_PAIR_DEVICE,
+ .send_param = pair_device_invalid_param_2,
+ .send_len = sizeof(pair_device_invalid_param_2),
+ .expect_status = MGMT_STATUS_INVALID_PARAMS,
+ .expect_param = pair_device_invalid_param_rsp_2,
+ .expect_len = sizeof(pair_device_invalid_param_rsp_2),
+};
+
static const void *pair_device_send_param_func(uint16_t *len)
{
struct test_data *data = tester_get_data();
@@ -2521,6 +2544,7 @@
.expect_hci_func = client_bdaddr_param_func,
.io_cap = 0x03, /* NoInputNoOutput */
.client_io_cap = 0x03, /* NoInputNoOutput */
+ .just_works = true,
};
static const struct generic_data pairing_acceptor_ssp_2 = {
@@ -2534,6 +2558,18 @@
.client_io_cap = 0x01, /* DisplayYesNo */
};
+static const struct generic_data pairing_acceptor_ssp_3 = {
+ .setup_settings = settings_powered_connectable_pairable_ssp,
+ .client_enable_ssp = true,
+ .expect_alt_ev = MGMT_EV_NEW_LINK_KEY,
+ .expect_alt_ev_len = 26,
+ .expect_hci_command = BT_HCI_CMD_USER_CONFIRM_REQUEST_REPLY,
+ .expect_hci_func = client_bdaddr_param_func,
+ .io_cap = 0x01, /* DisplayYesNo */
+ .client_io_cap = 0x01, /* DisplayYesNo */
+ .just_works = true,
+};
+
static const char unpair_device_param[] = {
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x00, 0x00 };
static const char unpair_device_rsp[] = {
@@ -2737,6 +2773,67 @@
.expect_status = MGMT_STATUS_INVALID_PARAMS,
};
+static const void *get_conn_info_send_param_func(uint16_t *len)
+{
+ struct test_data *data = tester_get_data();
+ static uint8_t param[7];
+
+ memcpy(param, hciemu_get_client_bdaddr(data->hciemu), 6);
+ param[6] = 0x00; /* Address type */
+
+ *len = sizeof(param);
+
+ return param;
+}
+
+static const void *get_conn_info_expect_param_func(uint16_t *len)
+{
+ struct test_data *data = tester_get_data();
+ static uint8_t param[10];
+
+ memcpy(param, hciemu_get_client_bdaddr(data->hciemu), 6);
+ param[6] = 0x00; /* Address type */
+ param[7] = 0xff; /* RSSI (= -1) */
+ param[8] = 0xff; /* TX power (= -1) */
+ param[9] = 0x04; /* max TX power */
+
+ *len = sizeof(param);
+
+ return param;
+}
+
+static const void *get_conn_info_error_expect_param_func(uint16_t *len)
+{
+ struct test_data *data = tester_get_data();
+ static uint8_t param[10];
+
+ /* All unset parameters shall be 0 in case of error */
+ memset(param, 0, sizeof(param));
+
+ memcpy(param, hciemu_get_client_bdaddr(data->hciemu), 6);
+ param[6] = 0x00; /* Address type */
+
+ *len = sizeof(param);
+
+ return param;
+}
+
+static const struct generic_data get_conn_info_succes1_test = {
+ .setup_settings = settings_powered_connectable_pairable_ssp,
+ .send_opcode = MGMT_OP_GET_CONN_INFO,
+ .send_func = get_conn_info_send_param_func,
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_func = get_conn_info_expect_param_func,
+};
+
+static const struct generic_data get_conn_info_ncon_test = {
+ .setup_settings = settings_powered_connectable_pairable_ssp,
+ .send_opcode = MGMT_OP_GET_CONN_INFO,
+ .send_func = get_conn_info_send_param_func,
+ .expect_status = MGMT_STATUS_NOT_CONNECTED,
+ .expect_func = get_conn_info_error_expect_param_func,
+};
+
static void client_cmd_complete(uint16_t opcode, uint8_t status,
const void *param, uint8_t len,
void *user_data)
@@ -3090,6 +3187,12 @@
struct mgmt_cp_user_confirm_reply cp;
uint16_t opcode;
+ if (test->just_works) {
+ tester_warn("User Confirmation received for just-works case");
+ tester_test_failed();
+ return;
+ }
+
memset(&cp, 0, sizeof(cp));
memcpy(&cp.addr, &ev->addr, sizeof(cp.addr));
@@ -3133,6 +3236,9 @@
if (test->client_auth_req)
bthost_set_auth_req(bthost, test->client_auth_req);
+ if (!test->just_works)
+ bthost_set_auth_req(bthost, 0x01);
+
if (test->client_reject_ssp)
bthost_set_reject_user_confirm(bthost, true);
@@ -3422,6 +3528,57 @@
bthost_hci_connect(bthost, master_bdaddr, addr_type);
}
+static void connected_event(uint16_t index, uint16_t length, const void *param,
+ void *user_data)
+{
+ struct test_data *data = tester_get_data();
+ const struct generic_data *test = data->test_data;
+ const void *send_param = test->send_param;
+ uint16_t send_len = test->send_len;
+
+ tester_print("Sending command 0x%04x", test->send_opcode);
+
+ if (test->send_func)
+ send_param = test->send_func(&send_len);
+
+ mgmt_send(data->mgmt, test->send_opcode, data->mgmt_index, send_len,
+ send_param, command_generic_callback, NULL, NULL);
+ test_add_condition(data);
+
+ /* Complete MGMT_EV_DEVICE_CONNECTED *after* adding new one */
+ test_condition_complete(data);
+}
+
+static void test_command_generic_connect(const void *test_data)
+{
+ struct test_data *data = tester_get_data();
+ unsigned int id;
+ const uint8_t *master_bdaddr;
+ uint8_t addr_type;
+ struct bthost *bthost;
+
+ tester_print("Registering %s notification",
+ mgmt_evstr(MGMT_EV_DEVICE_CONNECTED));
+ id = mgmt_register(data->mgmt_alt, MGMT_EV_DEVICE_CONNECTED,
+ data->mgmt_index, connected_event,
+ NULL, NULL);
+ data->mgmt_alt_ev_id = id;
+ test_add_condition(data);
+
+ master_bdaddr = hciemu_get_master_bdaddr(data->hciemu);
+ if (!master_bdaddr) {
+ tester_warn("No master bdaddr");
+ tester_test_failed();
+ return;
+ }
+
+ addr_type = data->hciemu_type == HCIEMU_TYPE_BREDRLE ? BDADDR_BREDR :
+ BDADDR_LE_PUBLIC;
+
+ bthost = hciemu_client_get_host(data->hciemu);
+ bthost_hci_connect(bthost, master_bdaddr, addr_type);
+}
+
int main(int argc, char *argv[])
{
tester_init(&argc, &argv);
@@ -3892,12 +4049,19 @@
&load_ltks_invalid_params_test_3,
NULL, test_command_generic);
+ test_bredrle("Set IO Capability - Invalid Params 1",
+ &set_io_cap_invalid_param_test_1,
+ NULL, test_command_generic);
+
test_bredrle("Pair Device - Not Powered 1",
&pair_device_not_powered_test_1,
NULL, test_command_generic);
test_bredrle("Pair Device - Invalid Parameters 1",
&pair_device_invalid_param_test_1,
NULL, test_command_generic);
+ test_bredrle("Pair Device - Invalid Parameters 2",
+ &pair_device_invalid_param_test_2,
+ NULL, test_command_generic);
test_bredrle("Pair Device - Legacy Success 1",
&pair_device_success_test_1,
NULL, test_command_generic);
@@ -3950,6 +4114,9 @@
test_bredrle("Pairing Acceptor - SSP 2",
&pairing_acceptor_ssp_2, setup_ssp_acceptor,
test_pairing_acceptor);
+ test_bredrle("Pairing Acceptor - SSP 3",
+ &pairing_acceptor_ssp_3, setup_ssp_acceptor,
+ test_pairing_acceptor);
test_bredrle("Unpair Device - Not Powered 1",
&unpair_device_not_powered_test_1,
@@ -4013,5 +4180,12 @@
&set_privacy_nval_param_test,
NULL, test_command_generic);
+ test_bredrle("Get Conn Info - Success",
+ &get_conn_info_succes1_test, NULL,
+ test_command_generic_connect);
+ test_bredrle("Get Conn Info - Not Connected",
+ &get_conn_info_ncon_test, NULL,
+ test_command_generic);
+
return tester_run();
}
diff --git a/tools/parser/avdtp.c b/tools/parser/avdtp.c
index 5a2ee55..5969067 100644
--- a/tools/parser/avdtp.c
+++ b/tools/parser/avdtp.c
@@ -150,6 +150,13 @@
return "Unknown";
}
+static char *vndcodec2str(uint32_t vendor, uint16_t vndcodec)
+{
+ if (vendor == 0x0000004f && vndcodec == 0x0001)
+ return "aptX";
+ return "Unknown";
+}
+
static char *cat2str(uint8_t cat)
{
switch (cat) {
@@ -212,12 +219,25 @@
len = get_u8(frm);
if (cat == 7) {
- uint8_t type, codec, tmp;
+ uint8_t type, codec;
+ uint16_t tmp, freq, vndcodec = 0;
+ uint32_t bitrate, vendor = 0;
+ int i;
type = get_u8(frm);
codec = get_u8(frm);
- printf("%s - %s\n", cat2str(cat), codec2str(type, codec));
+ if (codec == 255) {
+ vendor = btohl(htonl(get_u32(frm)));
+ vndcodec = btohs(htons(get_u16(frm)));
+
+ printf("%s - %s (%s)\n", cat2str(cat),
+ codec2str(type, codec),
+ vndcodec2str(vendor, vndcodec));
+ } else {
+ printf("%s - %s\n", cat2str(cat),
+ codec2str(type, codec));
+ }
switch (codec) {
case 0:
@@ -269,6 +289,143 @@
p_indent(level + 1, frm);
printf("Bitpool Range %d-%d\n", tmp, get_u8(frm));
break;
+ case 1:
+ tmp = get_u8(frm);
+ p_indent(level + 1, frm);
+ printf("Layers: ");
+ if (tmp & 0x80)
+ printf("1 ");
+ if (tmp & 0x40)
+ printf("2 ");
+ if (tmp & 0x20)
+ printf("3 ");
+ printf("\n");
+ p_indent(level + 1, frm);
+ printf("CRC Protection: %s\n",
+ tmp & 0x10 ? "Yes" : "No");
+ p_indent(level + 1, frm);
+ if (tmp & 0x08)
+ printf("Mono ");
+ if (tmp & 0x04)
+ printf("DualChannel ");
+ if (tmp & 0x02)
+ printf("Stereo ");
+ if (tmp & 0x01)
+ printf("JointStereo ");
+ printf("\n");
+ tmp = get_u8(frm);
+ p_indent(level + 1, frm);
+ printf("Media Payload Format: RFC-2250 %s\n",
+ tmp & 0x40 ? "RFC-3119" : "");
+ p_indent(level + 1, frm);
+ if (tmp & 0x20)
+ printf("16kHz ");
+ if (tmp & 0x10)
+ printf("22.05kHz ");
+ if (tmp & 0x08)
+ printf("24kHz ");
+ if (tmp & 0x04)
+ printf("32kHz ");
+ if (tmp & 0x02)
+ printf("44.1kHz ");
+ if (tmp & 0x01)
+ printf("48kHz ");
+ printf("\n");
+ tmp = get_u16(frm);
+ p_indent(level + 1, frm);
+ printf("VBR: %s\n",
+ tmp & 0x8000 ? "Yes" : "No");
+ p_indent(level + 1, frm);
+ printf("Bit Rate Indexes: ");
+ if (tmp & 0x8000) {
+ printf("n/a");
+ } else {
+ for (i = 0; i < 15; i++, tmp >>= 1)
+ if (tmp & 0x0001)
+ printf("%d ", i);
+ }
+ printf("\n");
+ break;
+ case 2:
+ tmp = get_u8(frm);
+ p_indent(level + 1, frm);
+ if (tmp & 0x80)
+ printf("MPEG-2 AAC LC ");
+ if (tmp & 0x40)
+ printf("MPEG-4 AAC LC ");
+ if (tmp & 0x20)
+ printf("MPEG-4 AAC LTP ");
+ if (tmp & 0x10)
+ printf("MPEG-4 AAC scalable ");
+ printf("\n");
+ tmp = get_u16(frm);
+ freq = tmp >> 4;
+ p_indent(level + 1, frm);
+ if (freq & 0x0800)
+ printf("8kHz ");
+ if (freq & 0x0400)
+ printf("11.025kHz ");
+ if (freq & 0x0200)
+ printf("12kHz ");
+ if (freq & 0x0100)
+ printf("16kHz ");
+ if (freq & 0x0080)
+ printf("22.05kHz ");
+ if (freq & 0x0040)
+ printf("24kHz ");
+ if (freq & 0x0020)
+ printf("32kHz ");
+ if (freq & 0x0010)
+ printf("44.1kHz ");
+ if (freq & 0x0008)
+ printf("48kHz ");
+ if (freq & 0x0004)
+ printf("64kHz ");
+ if (freq & 0x0002)
+ printf("88.2kHz ");
+ if (freq & 0x0001)
+ printf("96kHz ");
+ printf("\n");
+ tmp >>= 2;
+ p_indent(level + 1, frm);
+ if (tmp & 0x02)
+ printf("1 ");
+ if (tmp & 0x01)
+ printf("2 ");
+ printf("Channels\n");
+ tmp = get_u8(frm);
+ bitrate = ((tmp & 0x7f) << 16) | get_u16(frm);
+ p_indent(level + 1, frm);
+ printf("%ubps ", bitrate);
+ printf("%s\n", tmp & 0x80 ? "VBR" : "");
+ break;
+ case 255:
+ if (vendor == 0x0000004f &&
+ vndcodec == 0x0001) {
+ tmp = get_u8(frm);
+ p_indent(level + 1, frm);
+ if (tmp & 0x80)
+ printf("16kHz ");
+ if (tmp & 0x40)
+ printf("32kHz ");
+ if (tmp & 0x20)
+ printf("44.1kHz ");
+ if (tmp & 0x10)
+ printf("48kHz ");
+ printf("\n");
+ p_indent(level + 1, frm);
+ if (tmp & 0x02)
+ printf("Stereo ");
+ if (tmp & 0x01)
+ printf("Mono ");
+ printf("\n");
+ break;
+ } else {
+ hex_dump(level + 1, frm, len - 8);
+ frm->ptr += (len - 8);
+ frm->len -= (len - 8);
+ }
+ break;
default:
hex_dump(level + 1, frm, len - 2);
frm->ptr += (len - 2);
diff --git a/tools/smp-tester.c b/tools/smp-tester.c
index e09c802..1e0e1b0 100644
--- a/tools/smp-tester.c
+++ b/tools/smp-tester.c
@@ -81,6 +81,7 @@
struct smp_data {
const struct smp_req_rsp *req;
size_t req_count;
+ bool mitm;
};
static void mgmt_debug(const char *str, void *user_data)
@@ -282,7 +283,7 @@
};
static const uint8_t smp_nval_req_3[] = { 0x01, 0xff };
-static const uint8_t smp_nval_req_3_rsp[] = { 0x05, 0x08 };
+static const uint8_t smp_nval_req_3_rsp[] = { 0x05, 0x0a };
static const struct smp_req_rsp srv_nval_req_2[] = {
{ smp_nval_req_2, sizeof(smp_nval_req_3),
@@ -342,6 +343,29 @@
.req_count = G_N_ELEMENTS(cli_basic_req_1),
};
+static const uint8_t smp_basic_req_2[] = { 0x01, /* Pairing Request */
+ 0x04, /* NoInputNoOutput */
+ 0x00, /* OOB Flag */
+ 0x05, /* Bonding - MITM */
+ 0x10, /* Max key size */
+ 0x05, /* Init. key dist. */
+ 0x05, /* Rsp. key dist. */
+};
+static const struct smp_req_rsp cli_basic_req_2[] = {
+ { NULL, 0, smp_basic_req_2, sizeof(smp_basic_req_2) },
+ { smp_basic_req_1_rsp, sizeof(smp_basic_req_1_rsp),
+ smp_confirm_req_1, sizeof(smp_confirm_req_1) },
+ { smp_confirm_req_1, sizeof(smp_confirm_req_1),
+ smp_random_req_1, sizeof(smp_random_req_1) },
+ { smp_random_req_1, sizeof(smp_random_req_1), NULL, 0 },
+};
+
+static const struct smp_data smp_client_basic_req_2_test = {
+ .req = cli_basic_req_2,
+ .req_count = G_N_ELEMENTS(cli_basic_req_1),
+ .mitm = true,
+};
+
static void client_connectable_complete(uint16_t opcode, uint8_t status,
const void *param, uint8_t len,
void *user_data)
@@ -601,6 +625,7 @@
static void test_client(const void *test_data)
{
struct test_data *data = tester_get_data();
+ const struct smp_data *smp = data->test_data;
struct mgmt_cp_pair_device cp;
struct bthost *bthost;
@@ -611,7 +636,10 @@
memcpy(&cp.addr.bdaddr, data->ra, sizeof(data->ra));
cp.addr.type = BDADDR_LE_PUBLIC;
- cp.io_cap = 0x03; /* NoInputNoOutput */
+ if (smp->mitm)
+ cp.io_cap = 0x04; /* KeyboardDisplay */
+ else
+ cp.io_cap = 0x03; /* NoInputNoOutput */
mgmt_send(data->mgmt, MGMT_OP_PAIR_DEVICE, data->mgmt_index,
sizeof(cp), &cp, pair_device_complete, NULL, NULL);
@@ -687,6 +715,9 @@
test_smp("SMP Client - Basic Request 1",
&smp_client_basic_req_1_test,
setup_powered_client, test_client);
+ test_smp("SMP Client - Basic Request 2",
+ &smp_client_basic_req_2_test,
+ setup_powered_client, test_client);
return tester_run();
}
diff --git a/unit/test-avrcp.c b/unit/test-avrcp.c
index c41f2e6..e8cf34a 100644
--- a/unit/test-avrcp.c
+++ b/unit/test-avrcp.c
@@ -46,6 +46,7 @@
struct test_pdu {
bool valid;
bool fragmented;
+ bool continuing;
bool browse;
const uint8_t *data;
size_t size;
@@ -93,6 +94,14 @@
.size = sizeof(data(args)), \
}
+#define cont_pdu(args...) \
+ { \
+ .valid = true, \
+ .continuing = true, \
+ .data = data(args), \
+ .size = sizeof(data(args)), \
+ }
+
#define define_test(name, function, args...) \
do { \
const struct test_pdu pdus[] = { \
@@ -195,7 +204,8 @@
if (g_test_verbose())
util_hexdump('>', buf, len, test_debug, "AVRCP: ");
- g_assert_cmpint(len, ==, pdu->size);
+ if (!pdu->continuing)
+ g_assert_cmpint(len, ==, pdu->size);
g_assert(memcmp(buf, pdu->data, pdu->size) == 0);
@@ -486,9 +496,20 @@
uint64_t uid, uint8_t number,
uint32_t *attrs, void *user_data)
{
+ struct context *context = user_data;
+
DBG("");
- avrcp_get_element_attrs_rsp(session, transaction, NULL, 0);
+ if (g_str_has_prefix(context->data->test_name, "/TP/RCR")) {
+ uint8_t params[1024];
+
+ memset(params, 0x00, sizeof(params) / 2);
+ memset(params + (sizeof(params) / 2), 0xff, sizeof(params) / 2);
+
+ avrcp_get_element_attrs_rsp(session, transaction, params,
+ sizeof(params));
+ } else
+ avrcp_get_element_attrs_rsp(session, transaction, NULL, 0);
return -EAGAIN;
}
@@ -1623,5 +1644,43 @@
0x00, 0x19, 0x58, AVRCP_SET_ABSOLUTE_VOLUME,
0x00, 0x00, 0x01, 0x00));
+ /* Request continuing response - TG */
+ define_test("/TP/RCR/BV-02-C", test_server,
+ raw_pdu(0x00, 0x11, 0x0e, 0x01, 0x48, 0x00,
+ 0x00, 0x19, 0x58, AVRCP_GET_ELEMENT_ATTRIBUTES,
+ 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00),
+ cont_pdu(0x02, 0x11, 0x0e, 0x0c, 0x48, 0x00,
+ 0x00, 0x19, 0x58, AVRCP_GET_ELEMENT_ATTRIBUTES,
+ 0x01, 0x01, 0xf9),
+ raw_pdu(0x00, 0x11, 0x0e, 0x00, 0x48, 0x00,
+ 0x00, 0x19, 0x58, AVRCP_REQUEST_CONTINUING,
+ 0x00, 0x00, 0x01, AVRCP_GET_ELEMENT_ATTRIBUTES),
+ cont_pdu(0x02, 0x11, 0x0e, 0x0c, 0x48, 0x00,
+ 0x00, 0x19, 0x58, AVRCP_GET_ELEMENT_ATTRIBUTES,
+ 0x02, 0x01, 0xf9),
+ raw_pdu(0x00, 0x11, 0x0e, 0x00, 0x48, 0x00,
+ 0x00, 0x19, 0x58, AVRCP_REQUEST_CONTINUING,
+ 0x00, 0x00, 0x01, AVRCP_GET_ELEMENT_ATTRIBUTES),
+ cont_pdu(0x02, 0x11, 0x0e, 0x0c, 0x48, 0x00,
+ 0x00, 0x19, 0x58, AVRCP_GET_ELEMENT_ATTRIBUTES,
+ 0x03, 0x00, 0x0e));
+
+ /* Abort continuing response - TG */
+ define_test("/TP/RCR/BV-04-C", test_server,
+ raw_pdu(0x00, 0x11, 0x0e, 0x01, 0x48, 0x00,
+ 0x00, 0x19, 0x58, AVRCP_GET_ELEMENT_ATTRIBUTES,
+ 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00),
+ cont_pdu(0x02, 0x11, 0x0e, 0x0c, 0x48, 0x00,
+ 0x00, 0x19, 0x58, AVRCP_GET_ELEMENT_ATTRIBUTES,
+ 0x01, 0x01, 0xf9),
+ raw_pdu(0x00, 0x11, 0x0e, 0x00, 0x48, 0x00,
+ 0x00, 0x19, 0x58, AVRCP_ABORT_CONTINUING,
+ 0x00, 0x00, 0x01, AVRCP_GET_ELEMENT_ATTRIBUTES),
+ raw_pdu(0x02, 0x11, 0x0e, 0x09, 0x48, 0x00,
+ 0x00, 0x19, 0x58, AVRCP_ABORT_CONTINUING,
+ 0x00, 0x00, 0x00));
+
return g_test_run();
}
diff --git a/unit/test-crypto.c b/unit/test-crypto.c
new file mode 100644
index 0000000..fd2ea78
--- /dev/null
+++ b/unit/test-crypto.c
@@ -0,0 +1,169 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011 Intel Corporation
+ *
+ *
+ * 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 "src/shared/crypto.h"
+
+#include <string.h>
+#include <glib.h>
+
+static struct bt_crypto *crypto;
+
+struct test_data {
+ const uint8_t *msg;
+ uint16_t msg_len;
+ const uint8_t *t;
+};
+
+static const uint8_t key[] = {
+ 0x3c, 0x4f, 0xcf, 0x09, 0x88, 0x15, 0xf7, 0xab, 0xa6, 0xd2, 0xae, 0x28,
+ 0x16, 0x15, 0x7e, 0x2b
+};
+
+static const uint8_t msg_1[] = { 0x00 };
+
+static const uint8_t t_msg_1[] = {
+ 0x00, 0x00, 0x00, 0x00, 0xb3, 0xa8, 0x59, 0x41, 0x27, 0xeb, 0xc2, 0xc0
+};
+
+static const struct test_data test_data_1 = {
+ .msg = msg_1,
+ .msg_len = 0,
+ .t = t_msg_1
+};
+
+static const uint8_t msg_2[] = {
+ 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11,
+ 0x73, 0x93, 0x17, 0x2a
+
+};
+
+static const uint8_t t_msg_2[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x27, 0x39, 0x74, 0xf4, 0x39, 0x2a, 0x23, 0x2a
+};
+
+static const struct test_data test_data_2 = {
+ .msg = msg_2,
+ .msg_len = 16,
+ .t = t_msg_2
+};
+
+static const uint8_t msg_3[] = {
+ 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11,
+ 0x73, 0x93, 0x17, 0x2a, 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c,
+ 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, 0x30, 0xc8, 0x1c, 0x46,
+ 0xa3, 0x5c, 0xe4, 0x11
+};
+
+static const uint8_t t_msg_3[12] = {
+ 0x00, 0x00, 0x00, 0x00, 0xb7, 0xca, 0x94, 0xab, 0x87, 0xc7, 0x82, 0x18
+};
+
+static const struct test_data test_data_3 = {
+ .msg = msg_3,
+ .msg_len = 40,
+ .t = t_msg_3
+};
+
+static const uint8_t msg_4[] = {
+ 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11,
+ 0x73, 0x93, 0x17, 0x2a, 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c,
+ 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, 0x30, 0xc8, 0x1c, 0x46,
+ 0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
+ 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b,
+ 0xe6, 0x6c, 0x37, 0x10
+};
+
+static const uint8_t t_msg_4[12] = {
+ 0x00, 0x00, 0x00, 0x00, 0x44, 0xe1, 0xe6, 0xce, 0x1d, 0xf5, 0x13, 0x68
+};
+
+static const struct test_data test_data_4 = {
+ .msg = msg_4,
+ .msg_len = 64,
+ .t = t_msg_4
+};
+
+static void print_buf(const uint8_t *t, uint8_t len)
+{
+ int i;
+
+ for (i = 0; i < len; i++)
+ g_print("0x%02x, ", t[i]);
+
+ g_print("\n");
+}
+
+static bool result_compare(const uint8_t exp[12], uint8_t res[12])
+{
+ int i;
+ for (i = 0; i < 12; i++)
+ if (exp[i] != res[i])
+ return false;
+
+ return true;
+}
+
+static void test_sign(gconstpointer data)
+{
+ uint8_t t[12];
+ const struct test_data *d = data;
+
+ memset(t, 0, 12);
+ if (!bt_crypto_sign_att(crypto, key, d->msg, d->msg_len, 0, t))
+ g_assert(true);
+
+ if (g_test_verbose()) {
+ g_print("Result T: ");
+ print_buf(t, 12);
+ g_print("Expected T:");
+ print_buf(d->t, 12);
+ }
+
+ g_assert(result_compare(d->t, t));
+}
+
+int main(int argc, char *argv[])
+{
+ int exit_status;
+
+ crypto = bt_crypto_new();
+ if (!crypto)
+ return 0;
+
+ g_test_init(&argc, &argv, NULL);
+
+ g_test_add_data_func("/crypto/sign_att_1", &test_data_1, test_sign);
+ g_test_add_data_func("/crypto/sign_att_2", &test_data_2, test_sign);
+ g_test_add_data_func("/crypto/sign_att_3", &test_data_3, test_sign);
+ g_test_add_data_func("/crypto/sign_att_4", &test_data_4, test_sign);
+
+ exit_status = g_test_run();
+
+ bt_crypto_unref(crypto);
+
+ return exit_status;
+}
diff --git a/unit/test-hfp.c b/unit/test-hfp.c
index 20aa0b5..a8801b0 100644
--- a/unit/test-hfp.c
+++ b/unit/test-hfp.c
@@ -465,6 +465,9 @@
'\r'),
type_pdu(HFP_GW_CMD_TYPE_SET, 0),
data_end());
+ define_test("/hfp/test_empty", test_fragmented, NULL,
+ raw_pdu('\r'),
+ data_end());
return g_test_run();
}
diff --git a/unit/test-queue.c b/unit/test-queue.c
index 7c6d2ad..af7dfd3 100644
--- a/unit/test-queue.c
+++ b/unit/test-queue.c
@@ -58,11 +58,53 @@
queue_destroy(queue, NULL);
}
+static void foreach_destroy(void *data, void *user_data)
+{
+ struct queue *queue = user_data;
+
+ queue_destroy(queue, NULL);
+}
+
+static void test_foreach_destroy(void)
+{
+ struct queue *queue;
+
+ queue = queue_new();
+ g_assert(queue != NULL);
+
+ queue_push_tail(queue, UINT_TO_PTR(1));
+ queue_push_tail(queue, UINT_TO_PTR(2));
+
+ queue_foreach(queue, foreach_destroy, queue);
+}
+
+static void foreach_remove_all(void *data, void *user_data)
+{
+ struct queue *queue = user_data;
+
+ queue_remove_all(queue, NULL, NULL, NULL);
+}
+
+static void test_foreach_remove_all(void)
+{
+ struct queue *queue;
+
+ queue = queue_new();
+ g_assert(queue != NULL);
+
+ queue_push_tail(queue, UINT_TO_PTR(1));
+ queue_push_tail(queue, UINT_TO_PTR(2));
+
+ queue_foreach(queue, foreach_remove_all, queue);
+}
+
int main(int argc, char *argv[])
{
g_test_init(&argc, &argv, NULL);
g_test_add_func("/queue/basic", test_basic);
+ g_test_add_func("/queue/foreach_destroy", test_foreach_destroy);
+ g_test_add_func("/queue/foreach_remove_all", test_foreach_remove_all);
return g_test_run();
}
diff --git a/unit/test-uhid.c b/unit/test-uhid.c
new file mode 100644
index 0000000..85e1356
--- /dev/null
+++ b/unit/test-uhid.c
@@ -0,0 +1,310 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2014 Intel Corporation. All rights reserved.
+ *
+ *
+ * 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 <unistd.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <inttypes.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+
+#include <glib.h>
+
+#include "src/shared/uhid.h"
+#include "src/shared/util.h"
+
+struct test_pdu {
+ bool valid;
+ const uint8_t *data;
+ size_t size;
+};
+
+struct test_data {
+ char *test_name;
+ struct test_pdu *pdu_list;
+};
+
+struct context {
+ GMainLoop *main_loop;
+ struct bt_uhid *uhid;
+ guint source;
+ guint process;
+ int fd;
+ unsigned int pdu_offset;
+ const struct test_data *data;
+};
+
+#define event(args...) \
+ { \
+ .valid = true, \
+ .data = (void *) args, \
+ .size = sizeof(*args), \
+ }
+
+#define define_test(name, function, args...) \
+ do { \
+ const struct test_pdu pdus[] = { \
+ args, { } \
+ }; \
+ static struct test_data data; \
+ data.test_name = g_strdup(name); \
+ data.pdu_list = g_malloc(sizeof(pdus)); \
+ memcpy(data.pdu_list, pdus, sizeof(pdus)); \
+ g_test_add_data_func(name, &data, function); \
+ } while (0)
+
+static void test_debug(const char *str, void *user_data)
+{
+ const char *prefix = user_data;
+
+ g_print("%s%s\n", prefix, str);
+}
+
+static void test_free(gconstpointer user_data)
+{
+ const struct test_data *data = user_data;
+
+ g_free(data->test_name);
+ g_free(data->pdu_list);
+}
+
+static gboolean context_quit(gpointer user_data)
+{
+ struct context *context = user_data;
+
+ if (context->process > 0)
+ g_source_remove(context->process);
+
+ g_main_loop_quit(context->main_loop);
+
+ return FALSE;
+}
+
+static gboolean send_pdu(gpointer user_data)
+{
+ struct context *context = user_data;
+ const struct test_pdu *pdu;
+ ssize_t len;
+
+ pdu = &context->data->pdu_list[context->pdu_offset++];
+
+ len = write(context->fd, pdu->data, pdu->size);
+
+ if (g_test_verbose())
+ util_hexdump('<', pdu->data, len, test_debug, "uHID: ");
+
+ g_assert_cmpint(len, ==, pdu->size);
+
+ context->process = 0;
+ return FALSE;
+}
+
+static void context_process(struct context *context)
+{
+ if (!context->data->pdu_list[context->pdu_offset].valid) {
+ context_quit(context);
+ return;
+ }
+
+ context->process = g_idle_add(send_pdu, context);
+}
+
+static gboolean test_handler(GIOChannel *channel, GIOCondition cond,
+ gpointer user_data)
+{
+ struct context *context = user_data;
+ const struct test_pdu *pdu;
+ unsigned char buf[sizeof(struct uhid_event)];
+ ssize_t len;
+ int fd;
+
+ pdu = &context->data->pdu_list[context->pdu_offset++];
+
+ if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
+ context->source = 0;
+ g_print("%s: cond %x\n", __func__, cond);
+ return FALSE;
+ }
+
+ fd = g_io_channel_unix_get_fd(channel);
+
+ len = read(fd, buf, sizeof(buf));
+
+ g_assert(len > 0);
+
+ if (g_test_verbose())
+ util_hexdump('>', buf, len, test_debug, "uHID: ");
+
+ g_assert_cmpint(len, ==, pdu->size);
+
+ g_assert(memcmp(buf, pdu->data, pdu->size) == 0);
+
+ context_process(context);
+
+ return TRUE;
+}
+
+static struct context *create_context(gconstpointer data)
+{
+ struct context *context = g_new0(struct context, 1);
+ GIOChannel *channel;
+ int err, sv[2];
+
+ context->main_loop = g_main_loop_new(NULL, FALSE);
+ g_assert(context->main_loop);
+
+ err = socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, sv);
+ g_assert(err == 0);
+
+ context->uhid = bt_uhid_new(sv[0]);
+ g_assert(context->uhid != NULL);
+
+ channel = g_io_channel_unix_new(sv[1]);
+
+ g_io_channel_set_close_on_unref(channel, TRUE);
+ g_io_channel_set_encoding(channel, NULL, NULL);
+ g_io_channel_set_buffered(channel, FALSE);
+
+ context->source = g_io_add_watch(channel,
+ G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+ test_handler, context);
+ g_assert(context->source > 0);
+
+ g_io_channel_unref(channel);
+
+ context->fd = sv[1];
+ context->data = data;
+
+ return context;
+}
+
+static void destroy_context(struct context *context)
+{
+ if (context->source > 0)
+ g_source_remove(context->source);
+
+ bt_uhid_unref(context->uhid);
+
+ g_main_loop_unref(context->main_loop);
+
+ test_free(context->data);
+ g_free(context);
+}
+
+static void execute_context(struct context *context)
+{
+ g_main_loop_run(context->main_loop);
+
+ destroy_context(context);
+}
+
+static const struct uhid_event ev_create = {
+ .type = UHID_CREATE,
+};
+
+static const struct uhid_event ev_destroy = {
+ .type = UHID_DESTROY,
+};
+
+static const struct uhid_event ev_feature_answer = {
+ .type = UHID_FEATURE_ANSWER,
+};
+
+static const struct uhid_event ev_input = {
+ .type = UHID_INPUT,
+};
+
+static const struct uhid_event ev_output = {
+ .type = UHID_OUTPUT,
+};
+
+static const struct uhid_event ev_feature = {
+ .type = UHID_FEATURE,
+};
+
+static void test_client(gconstpointer data)
+{
+ struct context *context = create_context(data);
+
+ if (g_str_equal(context->data->test_name, "/uhid/command/create"))
+ bt_uhid_send(context->uhid, &ev_create);
+
+ if (g_str_equal(context->data->test_name, "/uhid/command/destroy"))
+ bt_uhid_send(context->uhid, &ev_destroy);
+
+ if (g_str_equal(context->data->test_name,
+ "/uhid/command/feature_answer"))
+ bt_uhid_send(context->uhid, &ev_feature_answer);
+
+ if (g_str_equal(context->data->test_name, "/uhid/command/input"))
+ bt_uhid_send(context->uhid, &ev_input);
+
+ execute_context(context);
+}
+
+static void handle_output(struct uhid_event *ev, void *user_data)
+{
+ g_assert_cmpint(ev->type, ==, UHID_OUTPUT);
+
+ context_quit(user_data);
+}
+
+static void handle_feature(struct uhid_event *ev, void *user_data)
+{
+ g_assert_cmpint(ev->type, ==, UHID_FEATURE);
+
+ context_quit(user_data);
+}
+
+static void test_server(gconstpointer data)
+{
+ struct context *context = create_context(data);
+
+ bt_uhid_register(context->uhid, UHID_OUTPUT, handle_output, context);
+ bt_uhid_register(context->uhid, UHID_FEATURE, handle_feature, context);
+
+ g_idle_add(send_pdu, context);
+
+ execute_context(context);
+}
+
+int main(int argc, char *argv[])
+{
+ g_test_init(&argc, &argv, NULL);
+
+ define_test("/uhid/command/create", test_client, event(&ev_create));
+ define_test("/uhid/command/destroy", test_client, event(&ev_destroy));
+ define_test("/uhid/command/feature_answer", test_client,
+ event(&ev_feature_answer));
+ define_test("/uhid/command/input", test_client, event(&ev_input));
+
+ define_test("/uhid/event/output", test_server, event(&ev_output));
+ define_test("/uhid/event/feature", test_server, event(&ev_feature));
+
+ return g_test_run();
+}