CHROMIUM: Merge latest BlueZ upstream version 5.35+
Merged all changes from the latest BlueZ upstream repo up to change:
"monitor: Add support for showing index information updates" after the
5.35 release.
BUG=chromium:539282
diff --git a/ChangeLog b/ChangeLog
index fbd407a..1ab978d 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,23 @@
+ver 5.35:
+ Fix issue with connected devices after discovery.
+ Fix issue with profile support and LTK loading.
+ Fix issue with AVRCP events for volume control.
+ Fix issue with OBEX session owner handling.
+ Fix issue with HID over GATT setup failures.
+ Fix issue with GATT notification registration.
+ Fix issue with GATT cache validation feature.
+ Add support for persistent GATT database.
+ Add support for controller enabling option.
+
+ver 5.34:
+ Fix issue with GATT profiles and auto-connect.
+ Fix issue with missing GoepL2CapPsm SDP data.
+ Fix issue with suspending AVDTP endpoints.
+ Fix issue with audio service state on disconnect.
+ Add support for AVRCP Set Addressed Player feature.
+ Add support for AVRCP Get Folder Items feature.
+ Add support for Android 5.1 HFP WBS callbacks.
+
ver 5.33:
Fix issue with memory leak in GATT database.
Fix issue with AVDTP set configuration handling.
diff --git a/Makefile.am b/Makefile.am
index ad4a9ae..d74187a 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 21:6:18
+lib_libbluetooth_la_LDFLAGS = $(AM_LDFLAGS) -version-info 21:8:18
lib_libbluetooth_la_DEPENDENCIES = $(local_headers)
endif
@@ -257,6 +257,9 @@
EXTRA_DIST += doc/obex-api.txt doc/obex-agent-api.txt
+EXTRA_DIST += doc/pics-opp.txt doc/pixit-opp.txt \
+ doc/pts-opp.txt
+
EXTRA_DIST += tools/magic.btsnoop
AM_CFLAGS += @DBUS_CFLAGS@ @GLIB_CFLAGS@
@@ -425,6 +428,12 @@
AM_TESTS_ENVIRONMENT += dbus-run-session --
endif
+if VALGRIND
+LOG_COMPILER = valgrind --error-exitcode=1 --num-callers=30
+LOG_FLAGS = --trace-children=yes --leak-check=full --show-reachable=no \
+ --suppressions=$(srcdir)/tools/valgrind.supp --quiet
+endif
+
pkgconfigdir = $(libdir)/pkgconfig
if LIBRARY
diff --git a/Makefile.tools b/Makefile.tools
index af5853e..c313813 100644
--- a/Makefile.tools
+++ b/Makefile.tools
@@ -412,4 +412,5 @@
test/test-heartrate test/test-alert test/test-hfp \
test/test-cyclingspeed test/opp-client test/ftp-client \
test/pbap-client test/map-client test/example-advertisement \
- test/example-gatt-server test/example-gatt-client
+ test/example-gatt-server test/example-gatt-client \
+ test/test-gatt-profile
diff --git a/TODO b/TODO
index 2945392..8c710ef 100644
--- a/TODO
+++ b/TODO
@@ -61,6 +61,12 @@
Priority: Low
Complexity: C2
+- Add queueing support for src/agent.c, currently if there is any request
+ pending the code fail with error EBUSY which is very inconvenient.
+
+ Priority: Low
+ Complexity: C2
+
Low Energy
==========
diff --git a/android/avdtp.c b/android/avdtp.c
index 7e61280..908c0ca 100644
--- a/android/avdtp.c
+++ b/android/avdtp.c
@@ -1567,7 +1567,7 @@
for (i = 0; i < seid_count; i++, seid++) {
failed_seid = seid->seid;
- sep = find_local_sep_by_seid(session, req->first_seid.seid);
+ sep = find_local_sep_by_seid(session, seid->seid);
if (!sep || !sep->stream) {
err = AVDTP_BAD_ACP_SEID;
goto failed;
@@ -1678,7 +1678,7 @@
for (i = 0; i < seid_count; i++, seid++) {
failed_seid = seid->seid;
- sep = find_local_sep_by_seid(session, req->first_seid.seid);
+ sep = find_local_sep_by_seid(session, seid->seid);
if (!sep || !sep->stream) {
err = AVDTP_BAD_ACP_SEID;
goto failed;
diff --git a/android/bluetoothd-snoop.c b/android/bluetoothd-snoop.c
index 035875a..75f58cc 100644
--- a/android/bluetoothd-snoop.c
+++ b/android/bluetoothd-snoop.c
@@ -73,6 +73,9 @@
case BTSNOOP_OPCODE_SCO_TX_PKT:
case BTSNOOP_OPCODE_SCO_RX_PKT:
break;
+ case BTSNOOP_OPCODE_OPEN_INDEX:
+ case BTSNOOP_OPCODE_CLOSE_INDEX:
+ break;
}
return 0xff;
diff --git a/android/hal-handsfree.c b/android/hal-handsfree.c
index 279b26a..3847901 100644
--- a/android/hal-handsfree.c
+++ b/android/hal-handsfree.c
@@ -153,6 +153,16 @@
#endif
}
+static void handle_wbs(void *buf, uint16_t len, int fd)
+{
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ struct hal_ev_handsfree_wbs *ev = buf;
+
+ if (cbs->wbs_cb)
+ cbs->wbs_cb(ev->wbs, (bt_bdaddr_t *) (ev->bdaddr));
+#endif
+}
+
static void handle_chld(void *buf, uint16_t len, int fd)
{
struct hal_ev_handsfree_chld *ev = buf;
@@ -289,6 +299,8 @@
/* HAL_EV_HANDSFREE_HSP_KEY_PRESS */
{ handle_hsp_key_press, false,
sizeof(struct hal_ev_handsfree_hsp_key_press) },
+ /* HAL_EV_HANDSFREE_WBS */
+ { handle_wbs, false, sizeof(struct hal_ev_handsfree_wbs) },
};
static uint8_t get_mode(void)
diff --git a/android/hal-ipc-api.txt b/android/hal-ipc-api.txt
index 503b47a..e3b7798 100644
--- a/android/hal-ipc-api.txt
+++ b/android/hal-ipc-api.txt
@@ -1068,6 +1068,14 @@
Notification parameters: Remote address (6 octets)
+ Opcode 0x91 - WBS Command notification
+
+ Notification parameters: WBS types (1 octet)
+ Remote address (6 octets)
+
+ Valid WBS types: 0x00 = None
+ 0x01 = No
+ 0x02 = Yes
Bluetooth Advanced Audio HAL (ID 6)
Bluetooth Advanced Audio Sink HAL (ID 13)
diff --git a/android/hal-msg.h b/android/hal-msg.h
index 698f45a..ea79fa7 100644
--- a/android/hal-msg.h
+++ b/android/hal-msg.h
@@ -1638,6 +1638,12 @@
uint8_t bdaddr[6];
} __attribute__((packed));
+#define HAL_EV_HANDSFREE_WBS 0x91
+struct hal_ev_handsfree_wbs {
+ uint8_t wbs;
+ uint8_t bdaddr[6];
+} __attribute__((packed));
+
#define HAL_AVRCP_FEATURE_NONE 0x00
#define HAL_AVRCP_FEATURE_METADATA 0x01
#define HAL_AVRCP_FEATURE_ABSOLUTE_VOLUME 0x02
diff --git a/android/handsfree.c b/android/handsfree.c
index f297926..2f8a867 100644
--- a/android/handsfree.c
+++ b/android/handsfree.c
@@ -1067,6 +1067,7 @@
void *user_data)
{
struct hf_device *dev = user_data;
+ struct hal_ev_handsfree_wbs ev;
unsigned int val;
DBG("");
@@ -1085,6 +1086,12 @@
break;
}
+ ev.wbs = val;
+ bdaddr2android(&dev->bdaddr, ev.bdaddr);
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+ HAL_EV_HANDSFREE_WBS, sizeof(ev), &ev);
+
dev->proposed_codec = 0;
dev->negotiated_codec = val;
diff --git a/configure.ac b/configure.ac
index 8ea503c..83a0776 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,5 +1,5 @@
AC_PREREQ(2.60)
-AC_INIT(bluez, 5.33)
+AC_INIT(bluez, 5.35)
AM_INIT_AUTOMAKE([foreign subdir-objects color-tests silent-rules
tar-pax no-dist-gzip dist-xz])
@@ -35,9 +35,12 @@
if (test "$USE_MAINTAINER_MODE" = "yes"); then
AC_CHECK_PROG(enable_coverage, [lcov], [yes], [no])
AC_CHECK_PROG(enable_dbus_run_session, [dbus-run-session], [yes])
+ AC_CHECK_PROG(enable_valgrind, [valgrind], [yes])
+ AC_CHECK_HEADERS(valgrind/memcheck.h)
fi
AM_CONDITIONAL(COVERAGE, test "${enable_coverage}" = "yes")
AM_CONDITIONAL(DBUS_RUN_SESSION, test "${enable_dbus_run_session}" = "yes")
+AM_CONDITIONAL(VALGRIND, test "${enable_valgrind}" = "yes")
MISC_FLAGS
diff --git a/doc/media-api.txt b/doc/media-api.txt
index b17a151..b5ad2db 100644
--- a/doc/media-api.txt
+++ b/doc/media-api.txt
@@ -71,43 +71,43 @@
=======================
Service org.bluez
-Interface org.bluez.MediaControl1 [Deprecated]
+Interface org.bluez.MediaControl1
Object path [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX
-Methods void Play()
+Methods void Play() [Deprecated]
Resume playback.
- void Pause()
+ void Pause() [Deprecated]
Pause playback.
- void Stop()
+ void Stop() [Deprecated]
Stop playback.
- void Next()
+ void Next() [Deprecated]
Next item.
- void Previous()
+ void Previous() [Deprecated]
Previous item.
- void VolumeUp()
+ void VolumeUp() [Deprecated]
Adjust remote volume one step up
- void VolumeDown()
+ void VolumeDown() [Deprecated]
Adjust remote volume one step down
- void FastForward()
+ void FastForward() [Deprecated]
Fast forward playback, this action is only stopped
when another method in this interface is called.
- void Rewind()
+ void Rewind() [Deprecated]
Rewind playback, this action is only stopped
when another method in this interface is called.
@@ -116,6 +116,10 @@
boolean Connected [readonly]
+ object Player [readonly, optional]
+
+ Addressed Player object path.
+
MediaPlayer1 hierarchy
======================
diff --git a/doc/mgmt-api.txt b/doc/mgmt-api.txt
index 1ae9835..c89ab42 100644
--- a/doc/mgmt-api.txt
+++ b/doc/mgmt-api.txt
@@ -24,6 +24,7 @@
Linux kernel v3.17 Version 1.7
Linux kernel v3.19 Version 1.8
Linux kernel v4.1 Version 1.9
+Linux kernel v4.2 Version 1.10
Version 1.1 introduces Set Device ID command.
diff --git a/doc/pics-opp.txt b/doc/pics-opp.txt
new file mode 100644
index 0000000..fb4746d
--- /dev/null
+++ b/doc/pics-opp.txt
@@ -0,0 +1,187 @@
+OPP PICS for the PTS tool.
+
+PTS version: 6.2.0
+
+* - different than PTS defaults
+# - not yet implemented/supported
+
+M - mandatory
+O - optional
+
+ Roles
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_OPP_1_1 True (*) Role: Object Push Client (C.1)
+TSPC_OPP_1_2 True (*) Role: Object Push Server (C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support at least one of the defined roles.
+-------------------------------------------------------------------------------
+
+
+ Client Profile Version
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_OPP_1b_1 False Client supports OPP version 1.1. (C.1)
+TSPC_OPP_1b_2 True (*) Client supports OPP version 1.2. (C.1)
+-------------------------------------------------------------------------------
+C.1: It is mandatory to support at least one of the profile versions.
+-------------------------------------------------------------------------------
+
+
+ Client Application Features
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_OPP_2_1 True Client: Perform Service Discovery request (M)
+TSPC_OPP_2_2 True Client: Authentication/PIN exchange supported.
+ (M)
+TSPC_OPP_2_2a True (*) Client: Require Authentication/PIN by default.
+ (O)
+TSPC_OPP_2_3 True Client: Object Push (M)
+TSPC_OPP_2_4 True (*) Client: vCard 2.1 (C.3)
+TSPC_OPP_2_5 False Client: vCalender 1.0 (O)
+TSPC_OPP_2_6 False Client: vMsg as defined in IrMC 1.1 (O)
+TSPC_OPP_2_7 False Client: vNote as defined in IrMC 1.1 (O)
+TSPC_OPP_2_8 True (*) Client: Support content formats other than those
+ declared in TSPC_OPP_2_4 through
+ TSPC_OPP_2_7. (O)
+TSPC_OPP_2_8a False Client: Support specific set of other content
+ formats. (C.4)
+TSPC_OPP_2_8b True (*) Client: Support all content formats. (C.4)
+TSPC_OPP_2_9 True (*) Client: Push multiple vCard objects. (O)
+TSPC_OPP_2_9a True (*) Client: Push multiple vCard objects using
+ different PUT operations. (C.5)
+TSPC_OPP_2_9b False Client: Push multiple vCard objects using the
+ same PUT operation. (C.5)
+TSPC_OPP_2_10 True (*) Client: Push multiple vCalender objects. (O)
+TSPC_OPP_2_10a True Client: Push multiple vCalendar objects using
+ different PUT operations. (C.6)
+TSPC_OPP_2_10b False Client: Push multiple vCalendar objects using
+ the same PUT operation. (C.6)
+TSPC_OPP_2_11 True (*) Client: Push multiple vMsg objects. (O)
+TSPC_OPP_2_11a True (*) Client: Push multiple vMsg objects using
+ different PUT operations. (C.7)
+TSPC_OPP_2_11b False Client: Push multiple vMsg objects using the
+ same PUT operation. (C.7)
+TSPC_OPP_2_12 True (*) Client: Push multiple vNote objects. (O)
+TSPC_OPP_2_12a True (*) Client: Push multiple vNote objects using
+ different PUT operations. (C.8)
+TSPC_OPP_2_12b False Client: Push multiple vNote objects using the
+ same PUT operation. (C.8)
+TSPC_OPP_2_13 True (*) Client: Pull business card (O)
+TSPC_OPP_2_14 True (*) Client: vCard 2.1 (C.1)
+TSPC_OPP_2_15 True (*) Client: Exchange business card (O)
+TSPC_OPP_2_16 False Client: vCard 2.1 (C.2)
+TSPC_OPP_2_17 True (*) GOEP v2 (C.9)
+TSPC_OPP_2_18 True (*) GOEP v2 Backward Compatibility (C.9)
+TSPC_OPP_2_19 True (*) OBEX over L2CAP (C.9)
+TSPC_OPP_2_20 False OBEX Reliable Session (C.10)
+TSPC_OPP_2_21 False OBEX SRM (C.10)
+TSPC_OPP_2_22 False Send OBEX SRMP header (C.10)
+TSPC_OPP_2_23 False Receive OBEX SRMP header (C.11)
+-------------------------------------------------------------------------------
+C.1: Mandatory to Support IF (TSPC_OPP_2_13) Business Card Pull is supported.
+C.2: Mandatory to Support IF (TSPC_OPP_2_15) Business Card Exchange is
+ supported.
+C.3: vCard 2.1 support is required for devices containing phonebook
+ applications. vCard 2.1 support optional for other devices.
+C.4: Mandatory to support one of TSPC_OPP_2_8a or TSPC_OPP_2_8b if TSPC_OPP_2_8
+ is supported. Otherwise, both items are excluded.
+C.5: Mandatory to support at least one of TSPC_OPP_2_9a and TSPC_OPP_2_9b if
+ TSPC_OPP_2_9 is supported. Otherwise, both items are excluded.
+C.6: Mandatory to support at least one of TSPC_OPP_2_10a and TSPC_OPP_2_10b if
+ TSPC_OPP_2_10 is supported. Otherwise, both items are excluded.
+C.7: Mandatory to support at least one of TSPC_OPP_2_11a and TSPC_OPP_2_11b if
+ TSPC_OPP_2_11 is supported. Otherwise, both items are excluded.
+C.8: Mandatory to support at least one of TSPC_OPP_2_12a and TSPC_OPP_2_12b if
+ TSPC_OPP_2_12 is supported. Otherwise, both items are excluded.
+C.9: Mandatory if TSPC_OPP_1b_2 supported.
+C.10: Optional to support if TSPC_OPP_1b_2 supported else excluded.
+C.11: Mandatory if TSPC_OPP_17 and TSPC_OPP_21 supported else excluded.
+-------------------------------------------------------------------------------
+
+
+ Server Profile Version
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_OPP_2b_1 False Server supports OPP version 1.1.
+TSPC_OPP_2b_2 True (*) Server supports OPP version 1.2.
+-------------------------------------------------------------------------------
+C.1: It is mandatory to support at least one of the profile versions.
+-------------------------------------------------------------------------------
+
+
+ Server Application Features
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_OPP_3_1 True Server: Provide information on supported
+ contents type on service discovery
+ request. (M)
+TSPC_OPP_3_2 True Server: Authentication/PIN exchange supported.
+ (M)
+TSPC_OPP_3_3 True Server: Object Push (M)
+TSPC_OPP_3_3a True (*) Server: Receive multiple objects in the same
+ PUT operation. (O)
+TSPC_OPP_3_4 True (*) Server: vCard 2.1 (C.3)
+TSPC_OPP_3_5 False Server: vCalender 1.0 format (O)
+TSPC_OPP_3_6 False Server: vMsg as defined in IrMC 1.1 (O)
+TSPC_OPP_3_7 False Server: vNote as defined in IrMC 1.1 (O)
+TSPC_OPP_3_8 True (*) Server: Support content formats other than those
+ declared in TSPC_OPP_3_4 through
+ TSPC_OPP_3_7. (O)
+TSPC_OPP_3_8a False Server: Support specific set of other content
+ formats. (C.4)
+TSPC_OPP_3_8b True (*) Server: Support all content formats. (C.4)
+TSPC_OPP_3_9 True (*) Server: Object Push vCard reject. (O)
+TSPC_OPP_3_10 True (*) Server: Object Push vCal reject. (O)
+TSPC_OPP_3_11 True (*) Server: Object Push vMsg reject. (O)
+TSPC_OPP_3_12 True (*) Server: Object Push vNote reject. (O)
+TSPC_OPP_3_13 True (*) Server: Business card pull (O.1)
+TSPC_OPP_3_14 True (*) Server: vCard 2.1 (C.1)
+TSPC_OPP_3_15 True (*) Server: Business card pull reject. (O)
+TSPC_OPP_3_16 True (*) Server: Business card exchange (O.2)
+TSPC_OPP_3_17 True (*) Server: vCard 2.1 (C.2)
+TSPC_OPP_3_18 True (*) Server: Business card exchange reject. (O)
+TSPC_OPP_3_19 True (*) GOEP v2 (C.5)
+TSPC_OPP_3_20 True (*) GOEP v2 Backward Compatibility (C.5)
+TSPC_OPP_3_21 True (*) OBEX over L2CAP (C.5)
+TSPC_OPP_3_22 False OBEX Reliable Session (C.16)
+TSPC_OPP_3_23 False OBEX SRM (C.6)
+TSPC_OPP_3_24 False Send OBEX SRMP header (C.6)
+TSPC_OPP_3_25 False Receive OBEX SRMP header (C.7)
+-------------------------------------------------------------------------------
+O.1: IF NOT Supported, an error message must be sent on request for Business
+ Card Pull.
+O.2: IF NOT Supported, an error message must be sent on request for Business
+ Card Exchange.
+C.1: Mandatory to Support IF (TSPC_OPP_3_13) Business Card Pull is supported.
+C.2: Mandatory to Support IF (TSPC_OPP_3_16) Business Card Exchange is
+ supported.
+C.3: vCard 2.1 support is required for devices containing phonebook
+ applications. vCard 2.1 support optional for other devices.
+C.4: Mandatory to support one of TSPC_OPP_3_8a or TSPC_OPP_3_8b if TSPC_OPP_3_8
+ is supported. Otherwise, both items are excluded.
+C.5: Mandatory if TSPC_OPP_2b_2 supported.
+C.6: Optional to support if TSPC_OPP_2b_2 supported, else excluded.
+C.7: Mandatory if TSPC_OPP_3_19 and TSPC_OPP_3_23 supported else excluded.
+-------------------------------------------------------------------------------
+
+
+ Additional OPP Capabilities
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_OPP_4_1 False Abort-Push Operation (O)
+TSPC_OPP_4_2 False Intentionally Left Blank (N/A)
+TSPC_OPP_4_3 True (*) Multiple vCards transferred as a single vObject
+ (C.1)
+TSPC_OPP_4_4 True (*) Multiple vCards transfer (C.1)
+TSPC_OPP_4_5 True (*) vCards with multiple Phone Number Fields (C.1)
+TSPC_OPP_4_6 True (*) Push vCal to Different Time Zone Server (C.1)
+-------------------------------------------------------------------------------
+C.1: Optional if TSPC_OPP_1_2 is supported, otherwise excluded.
+-------------------------------------------------------------------------------
diff --git a/doc/pixit-opp.txt b/doc/pixit-opp.txt
new file mode 100644
index 0000000..b7461d6
--- /dev/null
+++ b/doc/pixit-opp.txt
@@ -0,0 +1,27 @@
+OPP PIXIT for the PTS tool.
+
+PTS version: 6.2.0
+
+* - different than PTS defaults
+& - should be set to IUT Bluetooth address
+
+ Required PIXIT settings
+-------------------------------------------------------------------------------
+Parameter Name Value
+-------------------------------------------------------------------------------
+TSPX_supported_extension bmp
+TSPX_unsupported_extension pts
+TSPX_client_class_of_device 100104
+TSPX_server_class_of_device 100104
+TSPX_auth_password 0000
+TSPX_auth_user_id PTS
+TSPX_l2cap_psm 1003
+TSPX_rfcomm_channel 8
+TSPX_no_confirmations FALSE
+TSPX_bd_addr_iut 112233445566 (*&)
+TSPX_delete_link_key FALSE
+TSPX_pin_code 0000
+TSPX_security_enabled FALSE
+TSPX_time_guard 300000
+TSPX_use_implicit_send TRUE
+-------------------------------------------------------------------------------
diff --git a/doc/pts-opp.txt b/doc/pts-opp.txt
new file mode 100644
index 0000000..832028c
--- /dev/null
+++ b/doc/pts-opp.txt
@@ -0,0 +1,119 @@
+PTS test results for OPP
+
+PTS version: 6.2.0
+Tested: 26-Aug-2015
+
+Results:
+PASS test passed
+FAIL test failed
+INC test is inconclusive
+N/A test is disabled due to PICS setup
+NONE test result is none
+
+-------------------------------------------------------------------------------
+Test Name Result Notes
+-------------------------------------------------------------------------------
+TC_CLIENT_BC_BV_02_I PASS
+TC_CLIENT_BC_BV_04_I PASS
+TC_CLIENT_BCE_BV_01_I PASS
+TC_CLIENT_BCE_BV_03_I N/A
+TC_CLIENT_BCE_BV_04_I PASS
+TC_CLIENT_BCE_BV_05_I PASS
+TC_CLIENT_BCE_BV_06_I PASS
+TC_CLIENT_BCE_BV_07_I PASS
+TC_CLIENT_BCP_BV_01_I PASS
+TC_CLIENT_BCP_BV_02_I PASS
+TC_CLIENT_BCP_BV_03_I N/A
+TC_CLIENT_BCP_BV_04_I PASS
+TC_CLIENT_BCP_BV_05_I PASS
+TC_CLIENT_CON_BV_01_C PASS
+TC_CLIENT_OPH_BI_01_C PASS
+TC_CLIENT_OPH_BV_01_I PASS
+TC_CLIENT_OPH_BV_02_I N/A
+TC_CLIENT_OPH_BV_03_I PASS
+TC_CLIENT_OPH_BV_04_I PASS
+TC_CLIENT_OPH_BV_05_I PASS
+TC_CLIENT_OPH_BV_07_I N/A
+TC_CLIENT_OPH_BV_08_I PASS
+TC_CLIENT_OPH_BV_09_I N/A
+TC_CLIENT_OPH_BV_10_I N/A
+TC_CLIENT_OPH_BV_11_I N/A
+TC_CLIENT_OPH_BV_12_I PASS
+TC_CLIENT_OPH_BV_13_I N/A
+TC_CLIENT_OPH_BV_14_I N/A
+TC_CLIENT_OPH_BV_15_I N/A
+TC_CLIENT_OPH_BV_16_I PASS
+TC_CLIENT_OPH_BV_17_I N/A
+TC_CLIENT_OPH_BV_18_I N/A
+TC_CLIENT_OPH_BV_19_I PASS Send file other than vCard
+TC_CLIENT_OPH_BV_20_I PASS
+TC_CLIENT_OPH_BV_22_I PASS Send file greater than 2 MB
+TC_CLIENT_OPH_BV_23_I N/A
+TC_CLIENT_OPH_BV_24_I N/A
+TC_CLIENT_OPH_BV_25_I N/A
+TC_CLIENT_OPH_BV_26_I N/A
+TC_CLIENT_SRM_BV_01_C N/A
+TC_CLIENT_SRM_BV_03_C N/A
+TC_CLIENT_SRM_BV_05_C N/A
+TC_CLIENT_SRM_BV_07_C N/A
+TC_CLIENT_SRMP_BI_01_C N/A
+TC_CLIENT_SRMP_BV_01_C N/A
+TC_CLIENT_SRMP_BV_04_C N/A
+TC_CLIENT_SRMP_BV_05_C N/A
+TC_CLIENT_SRMP_BV_06_C N/A
+TC_SERVER_BC_BV_01_I PASS
+TC_SERVER_BC_BV_03_I PASS
+TC_SERVER_BCE_BV_01_I PASS
+TC_SERVER_BCE_BV_03_I PASS
+TC_SERVER_BCE_BV_04_I PASS
+TC_SERVER_BCE_BV_05_I PASS
+TC_SERVER_BCE_BV_06_I PASS
+TC_SERVER_BCE_BV_07_I PASS
+TC_SERVER_BCP_BV_01_I PASS
+TC_SERVER_BCP_BV_02_I N/A
+TC_SERVER_BCP_BV_03_I PASS
+TC_SERVER_BCP_BV_04_I PASS
+TC_SERVER_BCP_BV_05_I PASS
+TC_SERVER_CON_BV_02_C PASS
+TC_SERVER_OPH_BV_01_I PASS
+TC_SERVER_OPH_BV_02_I PASS
+TC_SERVER_OPH_BV_03_I PASS
+TC_SERVER_OPH_BV_04_I PASS
+TC_SERVER_OPH_BV_05_I PASS
+TC_SERVER_OPH_BV_07_I N/A
+TC_SERVER_OPH_BV_08_I N/A
+TC_SERVER_OPH_BV_09_I PASS
+TC_SERVER_OPH_BV_10_I PASS
+TC_SERVER_OPH_BV_11_I N/A
+TC_SERVER_OPH_BV_12_I N/A
+TC_SERVER_OPH_BV_13_I PASS
+TC_SERVER_OPH_BV_14_I PASS
+TC_SERVER_OPH_BV_15_I N/A
+TC_SERVER_OPH_BV_16_I N/A
+TC_SERVER_OPH_BV_17_I PASS
+TC_SERVER_OPH_BV_18_I PASS
+TC_SERVER_OPH_BV_19_I PASS
+TC_SERVER_OPH_BV_21_I N/A
+TC_SERVER_OPH_BV_22_I PASS
+TC_SERVER_OPH_BV_23_I PASS
+TC_SERVER_OPH_BV_24_I N/A
+TC_SERVER_OPH_BV_25_I N/A
+TC_SERVER_OPH_BV_26_I N/A
+TC_SERVER_ROB_BV_01_C PASS
+TC_SERVER_ROB_BV_02_C PASS
+TC_SERVER_SRM_BI_02_C N/A
+TC_SERVER_SRM_BI_03_C PASS
+TC_SERVER_SRM_BI_05_C N/A
+TC_SERVER_SRM_BV_04_C N/A
+TC_SERVER_SRM_BV_08_C N/A
+TC_SERVER_SRMP_BV_02_C N/A
+TC_SERVER_SRMP_BV_03_C N/A
+TC_CLIENT_OPH_BV_27_I N/A
+TC_CLIENT_OPH_BV_34_I PASS
+TC_SERVER_OPH_BV_27_I N/A
+TC_SERVER_OPH_BV_30_I PASS
+TC_SERVER_OPH_BV_31_I PASS
+TC_SERVER_OPH_BV_32_I PASS
+TC_SERVER_OPH_BV_33_I N/A
+TC_SERVER_OPH_BV_34_I PASS
+-------------------------------------------------------------------------------
diff --git a/doc/settings-storage.txt b/doc/settings-storage.txt
index 6c9f058..aea50a6 100644
--- a/doc/settings-storage.txt
+++ b/doc/settings-storage.txt
@@ -139,11 +139,15 @@
============================
Each file, named by remote device address, may includes multiple groups
-(General and ServiceRecords).
+(General, ServiceRecords, Attributes).
In ServiceRecords, SDP records are stored using their handle as key
(hexadecimal format).
+In "Attributes" group GATT database is stored using attribute handle as key
+(hexadecimal format). Value associated with this handle is serialized form of
+all data required to re-create given attribute. ":" is used to separate fields.
+
[General] group contains:
Name String Remote device friendly name
@@ -155,6 +159,37 @@
<0x...> String SDP record as hexadecimal encoded
string
+In [Attributes] group value always starts with attribute type, that determines
+how to interpret rest of value:
+
+ Primary service:
+ 2800:end_handle:uuid
+
+ Secondary service:
+ 2801:end_handle:uuid
+
+ Included service:
+ 2802:start_handle:end_handle:uuid
+
+ Characteristic:
+ 2803:value_handle:properties:uuid
+
+ Descriptor:
+ uuid
+
+Sample Attributes section:
+ [Attributes]
+ 0001=2800:0005:1801
+ 0002=2803:0003:20:2a05
+ 0014=2800:001c:1800
+ 0015=2803:0016:02:2a00
+ 0017=2803:0018:02:2a01
+ 0019=2803:001a:02:2aa6
+ 0028=2800:ffff:0000180d-0000-1000-8000-00805f9b34fb
+ 0029=2803:002a:10:00002a37-0000-1000-8000-00805f9b34fb
+ 002b=2803:002c:02:00002a38-0000-1000-8000-00805f9b34fb
+ 002d=2803:002e:08:00002a39-0000-1000-8000-00805f9b34fb
+
Info file format
================
diff --git a/emulator/btdev.c b/emulator/btdev.c
index cd211ef..e8cead8 100644
--- a/emulator/btdev.c
+++ b/emulator/btdev.c
@@ -420,20 +420,33 @@
btdev->commands[25] |= 0x01; /* LE Set Event Mask */
btdev->commands[25] |= 0x02; /* LE Read Buffer Size */
btdev->commands[25] |= 0x04; /* LE Read Local Features */
+ btdev->commands[25] |= 0x10; /* LE Set Random Address */
btdev->commands[25] |= 0x20; /* LE Set Adv Parameters */
btdev->commands[25] |= 0x40; /* LE Read Adv TX Power */
btdev->commands[25] |= 0x80; /* LE Set Adv Data */
+ btdev->commands[26] |= 0x01; /* LE Set Scan Response Data */
btdev->commands[26] |= 0x02; /* LE Set Adv Enable */
btdev->commands[26] |= 0x04; /* LE Set Scan Parameters */
btdev->commands[26] |= 0x08; /* LE Set Scan Enable */
+ btdev->commands[26] |= 0x10; /* LE Create Connection */
btdev->commands[26] |= 0x40; /* LE Read White List Size */
+ btdev->commands[26] |= 0x80; /* LE Clear White List */
+ btdev->commands[27] |= 0x04; /* LE Connection Update */
+ btdev->commands[27] |= 0x20; /* LE Read Remote Used Features */
btdev->commands[27] |= 0x40; /* LE Encrypt */
btdev->commands[27] |= 0x80; /* LE Rand */
+ btdev->commands[28] |= 0x01; /* LE Start Encryption */
+ btdev->commands[28] |= 0x02; /* LE Long Term Key Request Reply */
+ btdev->commands[28] |= 0x04; /* LE Long Term Key Request Neg Reply */
btdev->commands[28] |= 0x08; /* LE Read Supported States */
btdev->commands[28] |= 0x10; /* LE Receiver Test */
btdev->commands[28] |= 0x20; /* LE Transmitter Test */
btdev->commands[28] |= 0x40; /* LE Test End */
+ /* Extra LE commands for >= 4.1 adapters */
+ btdev->commands[33] |= 0x10; /* LE Remote Conn Param Req Reply */
+ btdev->commands[33] |= 0x20; /* LE Remote Conn Param Req Neg Reply */
+
/* Extra LE commands for >= 4.2 adapters */
btdev->commands[34] |= 0x02; /* LE Read Local P-256 Public Key */
btdev->commands[34] |= 0x04; /* LE Generate DHKey */
@@ -553,6 +566,8 @@
btdev->max_page = 1;
btdev->le_features[0] |= 0x01; /* LE Encryption */
+ btdev->le_features[0] |= 0x02; /* Connection Parameters Request */
+ btdev->le_features[0] |= 0x08; /* Slave-initiated Features Exchange */
}
static void set_amp_features(struct btdev *btdev)
@@ -1086,8 +1101,8 @@
}
static void le_conn_complete(struct btdev *btdev,
- const uint8_t *bdaddr, uint8_t bdaddr_type,
- uint8_t status)
+ const struct bt_hci_cmd_le_create_conn *lecc,
+ 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];
@@ -1097,8 +1112,10 @@
buf[0] = BT_HCI_EVT_LE_CONN_COMPLETE;
if (!status) {
- struct btdev *remote = find_btdev_by_bdaddr_type(bdaddr,
- bdaddr_type);
+ struct btdev *remote;
+
+ remote = find_btdev_by_bdaddr_type(lecc->peer_addr,
+ lecc->peer_addr_type);
btdev->conn = remote;
btdev->le_adv_enable = 0;
@@ -1114,15 +1131,16 @@
cc->role = 0x01;
cc->handle = cpu_to_le16(42);
+ cc->interval = lecc->max_interval;
+ cc->latency = lecc->latency;
+ cc->supv_timeout = lecc->supv_timeout;
send_event(remote, BT_HCI_EVT_LE_META_EVENT, buf, sizeof(buf));
-
- cc->handle = cpu_to_le16(42);
}
cc->status = status;
- cc->peer_addr_type = bdaddr_type;
- memcpy(cc->peer_addr, bdaddr, 6);
+ cc->peer_addr_type = lecc->peer_addr_type;
+ memcpy(cc->peer_addr, lecc->peer_addr, 6);
cc->role = 0x00;
send_event(btdev, BT_HCI_EVT_LE_META_EVENT, buf, sizeof(buf));
@@ -1164,16 +1182,17 @@
return btdev->le_adv_type != 0x03;
}
-static void le_conn_request(struct btdev *btdev, const uint8_t *bdaddr,
- uint8_t bdaddr_type)
+static void le_conn_request(struct btdev *btdev,
+ const struct bt_hci_cmd_le_create_conn *lecc)
{
- struct btdev *remote = find_btdev_by_bdaddr_type(bdaddr, bdaddr_type);
+ struct btdev *remote = find_btdev_by_bdaddr_type(lecc->peer_addr,
+ lecc->peer_addr_type);
if (remote && adv_connectable(remote) && adv_match(btdev, remote) &&
- remote->le_adv_own_addr == bdaddr_type)
- le_conn_complete(btdev, bdaddr, bdaddr_type, 0);
+ remote->le_adv_own_addr == lecc->peer_addr_type)
+ le_conn_complete(btdev, lecc, 0);
else
- le_conn_complete(btdev, bdaddr, bdaddr_type,
+ le_conn_complete(btdev, lecc,
BT_HCI_ERR_CONN_FAILED_TO_ESTABLISH);
}
@@ -1194,8 +1213,27 @@
}
}
+static void rej_le_conn_update(struct btdev *btdev, uint16_t handle,
+ uint8_t reason)
+{
+ struct btdev *remote = btdev->conn;
+ struct __packed {
+ uint8_t subevent;
+ struct bt_hci_evt_le_conn_update_complete ev;
+ } ev;
+
+ if (!remote)
+ return;
+
+ ev.subevent = BT_HCI_EVT_LE_CONN_UPDATE_COMPLETE;
+ ev.ev.handle = cpu_to_le16(handle);
+ ev.ev.status = cpu_to_le16(reason);
+
+ send_event(remote, BT_HCI_EVT_LE_META_EVENT, &ev, sizeof(ev));
+}
+
static void le_conn_update(struct btdev *btdev, uint16_t handle,
- uint16_t max_interval, uint16_t min_interval,
+ uint16_t min_interval, uint16_t max_interval,
uint16_t latency, uint16_t supv_timeout,
uint16_t min_length, uint16_t max_length)
{
@@ -1222,6 +1260,30 @@
send_event(remote, BT_HCI_EVT_LE_META_EVENT, &ev, sizeof(ev));
}
+static void le_conn_param_req(struct btdev *btdev, uint16_t handle,
+ uint16_t min_interval, uint16_t max_interval,
+ uint16_t latency, uint16_t supv_timeout,
+ uint16_t min_length, uint16_t max_length)
+{
+ struct btdev *remote = btdev->conn;
+ struct __packed {
+ uint8_t subevent;
+ struct bt_hci_evt_le_conn_param_request ev;
+ } ev;
+
+ if (!remote)
+ return;
+
+ ev.subevent = BT_HCI_EVT_LE_CONN_PARAM_REQUEST;
+ ev.ev.handle = cpu_to_le16(handle);
+ ev.ev.min_interval = cpu_to_le16(min_interval);
+ ev.ev.max_interval = cpu_to_le16(max_interval);
+ ev.ev.latency = cpu_to_le16(latency);
+ ev.ev.supv_timeout = cpu_to_le16(supv_timeout);
+
+ send_event(remote, BT_HCI_EVT_LE_META_EVENT, &ev, sizeof(ev));
+}
+
static void disconnect_complete(struct btdev *btdev, uint16_t handle,
uint8_t reason)
{
@@ -1991,6 +2053,8 @@
const struct bt_hci_cmd_le_ltk_req_reply *llrr;
const struct bt_hci_cmd_le_encrypt *lenc_cmd;
const struct bt_hci_cmd_le_generate_dhkey *dh;
+ const struct bt_hci_cmd_le_conn_param_req_reply *lcprr_cmd;
+ const struct bt_hci_cmd_le_conn_param_req_neg_reply *lcprnr_cmd;
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;
@@ -2032,6 +2096,8 @@
struct bt_hci_rsp_read_local_amp_info rlai;
struct bt_hci_rsp_read_local_amp_assoc rlaa_rsp;
struct bt_hci_rsp_get_mws_transport_config *gmtc;
+ struct bt_hci_rsp_le_conn_param_req_reply lcprr_rsp;
+ struct bt_hci_rsp_le_conn_param_req_neg_reply lcprnr_rsp;
struct bt_hci_rsp_le_read_buffer_size lrbs;
struct bt_hci_rsp_le_read_local_features lrlf;
struct bt_hci_rsp_le_read_adv_tx_power lratp;
@@ -2939,8 +3005,6 @@
status = BT_HCI_ERR_SUCCESS;
}
cmd_complete(btdev, opcode, &status, sizeof(status));
- if (status == BT_HCI_ERR_SUCCESS && btdev->le_scan_enable)
- le_set_scan_enable_complete(btdev);
break;
case BT_HCI_CMD_LE_CREATE_CONN:
@@ -3136,6 +3200,22 @@
cmd_complete(btdev, opcode, <e, sizeof(lte));
break;
+ case BT_HCI_CMD_LE_CONN_PARAM_REQ_REPLY:
+ if (btdev->type == BTDEV_TYPE_BREDR)
+ goto unsupported;
+ lcprr_cmd = data;
+ lcprr_rsp.handle = lcprr_cmd->handle;
+ lcprr_rsp.status = BT_HCI_ERR_SUCCESS;
+ cmd_complete(btdev, opcode, &lcprr_rsp, sizeof(lcprr_rsp));
+ break;
+ case BT_HCI_CMD_LE_CONN_PARAM_REQ_NEG_REPLY:
+ if (btdev->type == BTDEV_TYPE_BREDR)
+ goto unsupported;
+ lcprnr_cmd = data;
+ lcprnr_rsp.handle = lcprnr_cmd->handle;
+ lcprnr_rsp.status = BT_HCI_ERR_SUCCESS;
+ cmd_complete(btdev, opcode, &lcprnr_rsp, sizeof(lcprnr_rsp));
+ break;
default:
goto unsupported;
}
@@ -3170,6 +3250,9 @@
const struct bt_hci_cmd_read_clock_offset *rco;
const struct bt_hci_cmd_le_create_conn *lecc;
const struct bt_hci_cmd_le_conn_update *lecu;
+ const struct bt_hci_cmd_le_conn_param_req_reply *lcprr;
+ const struct bt_hci_cmd_le_conn_param_req_neg_reply *lcprnr;
+ const struct bt_hci_cmd_le_set_scan_enable *lsse;
switch (opcode) {
case BT_HCI_CMD_INQUIRY:
@@ -3314,21 +3397,57 @@
return;
lecc = data;
btdev->le_scan_own_addr_type = lecc->own_addr_type;
- le_conn_request(btdev, lecc->peer_addr, lecc->peer_addr_type);
+ le_conn_request(btdev, lecc);
break;
case BT_HCI_CMD_LE_CONN_UPDATE:
if (btdev->type == BTDEV_TYPE_BREDR)
return;
lecu = data;
- le_conn_update(btdev, le16_to_cpu(lecu->handle),
- le16_to_cpu(lecu->min_interval),
- le16_to_cpu(lecu->max_interval),
- le16_to_cpu(lecu->latency),
- le16_to_cpu(lecu->supv_timeout),
- le16_to_cpu(lecu->min_length),
- le16_to_cpu(lecu->max_length));
+ if (btdev->le_features[0] & 0x02)
+ le_conn_param_req(btdev, le16_to_cpu(lecu->handle),
+ le16_to_cpu(lecu->min_interval),
+ le16_to_cpu(lecu->max_interval),
+ le16_to_cpu(lecu->latency),
+ le16_to_cpu(lecu->supv_timeout),
+ le16_to_cpu(lecu->min_length),
+ le16_to_cpu(lecu->max_length));
+ else
+ le_conn_update(btdev, le16_to_cpu(lecu->handle),
+ le16_to_cpu(lecu->min_interval),
+ le16_to_cpu(lecu->max_interval),
+ le16_to_cpu(lecu->latency),
+ le16_to_cpu(lecu->supv_timeout),
+ le16_to_cpu(lecu->min_length),
+ le16_to_cpu(lecu->max_length));
break;
+ case BT_HCI_CMD_LE_CONN_PARAM_REQ_REPLY:
+ if (btdev->type == BTDEV_TYPE_BREDR)
+ return;
+ lcprr = data;
+ le_conn_update(btdev, le16_to_cpu(lcprr->handle),
+ le16_to_cpu(lcprr->min_interval),
+ le16_to_cpu(lcprr->max_interval),
+ le16_to_cpu(lcprr->latency),
+ le16_to_cpu(lcprr->supv_timeout),
+ le16_to_cpu(lcprr->min_length),
+ le16_to_cpu(lcprr->max_length));
+ break;
+ case BT_HCI_CMD_LE_CONN_PARAM_REQ_NEG_REPLY:
+ if (btdev->type == BTDEV_TYPE_BREDR)
+ return;
+ lcprnr = data;
+ rej_le_conn_update(btdev, le16_to_cpu(lcprnr->handle),
+ le16_to_cpu(lcprnr->reason));
+ break;
+ break;
+ case BT_HCI_CMD_LE_SET_SCAN_ENABLE:
+ if (btdev->type == BTDEV_TYPE_BREDR)
+ return;
+ lsse = data;
+ if (btdev->le_scan_enable && lsse->enable)
+ le_set_scan_enable_complete(btdev);
+
}
}
diff --git a/gdbus/gdbus.h b/gdbus/gdbus.h
index d99c254..9ece4b0 100644
--- a/gdbus/gdbus.h
+++ b/gdbus/gdbus.h
@@ -35,6 +35,7 @@
typedef enum GDBusSignalFlags GDBusSignalFlags;
typedef enum GDBusPropertyFlags GDBusPropertyFlags;
typedef enum GDBusSecurityFlags GDBusSecurityFlags;
+typedef enum GDbusPropertyChangedFlags GDbusPropertyChangedFlags;
typedef struct GDBusArgInfo GDBusArgInfo;
typedef struct GDBusMethodTable GDBusMethodTable;
@@ -115,6 +116,10 @@
G_DBUS_SECURITY_FLAG_ALLOW_INTERACTION = (1 << 2),
};
+enum GDbusPropertyChangedFlags {
+ G_DBUS_PROPERTY_CHANGED_FLAG_FLUSH = (1 << 0),
+};
+
struct GDBusArgInfo {
const char *name;
const char *signature;
@@ -300,6 +305,10 @@
void g_dbus_emit_property_changed(DBusConnection *connection,
const char *path, const char *interface,
const char *name);
+void g_dbus_emit_property_changed_full(DBusConnection *connection,
+ const char *path, const char *interface,
+ const char *name,
+ GDbusPropertyChangedFlags flags);
gboolean g_dbus_get_properties(DBusConnection *connection, const char *path,
const char *interface, DBusMessageIter *iter);
diff --git a/gdbus/object.c b/gdbus/object.c
index 96db516..4cf2e2f 100644
--- a/gdbus/object.c
+++ b/gdbus/object.c
@@ -1720,9 +1720,10 @@
}
}
-void g_dbus_emit_property_changed(DBusConnection *connection,
+void g_dbus_emit_property_changed_full(DBusConnection *connection,
const char *path, const char *interface,
- const char *name)
+ const char *name,
+ GDbusPropertyChangedFlags flags)
{
const GDBusPropertyTable *property;
struct generic_data *data;
@@ -1760,7 +1761,16 @@
iface->pending_prop = g_slist_prepend(iface->pending_prop,
(void *) property);
- add_pending(data);
+ if (flags & G_DBUS_PROPERTY_CHANGED_FLAG_FLUSH)
+ process_property_changes(data);
+ else
+ add_pending(data);
+}
+
+void g_dbus_emit_property_changed(DBusConnection *connection, const char *path,
+ const char *interface, const char *name)
+{
+ g_dbus_emit_property_changed_full(connection, path, interface, name, 0);
}
gboolean g_dbus_get_properties(DBusConnection *connection, const char *path,
diff --git a/lib/bluetooth.c b/lib/bluetooth.c
index 301e6ab..1142fd3 100644
--- a/lib/bluetooth.c
+++ b/lib/bluetooth.c
@@ -810,7 +810,7 @@
case 273:
return "Steelseries ApS";
case 274:
- return "vyzybl Inc.";
+ return "Visybl Inc.";
case 275:
return "Openbrain Technologies, Co., Ltd.";
case 276:
@@ -1489,6 +1489,96 @@
return "DDS, Inc.";
case 613:
return "SMK Corporation";
+ case 614:
+ return "Schawbel Technologies LLC";
+ case 615:
+ return "XMI Systems SA";
+ case 616:
+ return "Cerevo";
+ case 617:
+ return "Torrox GmbH & Co KG";
+ case 618:
+ return "Gemalto";
+ case 619:
+ return "DEKA Research & Development Corp.";
+ case 620:
+ return "Domster Tadeusz Szydlowski";
+ case 621:
+ return "Technogym SPA";
+ case 622:
+ return "FLEURBAEY BVBA";
+ case 623:
+ return "Aptcode Solutions";
+ case 624:
+ return "LSI ADL Technology";
+ case 625:
+ return "Animas Corp";
+ case 626:
+ return "Alps Electric Co., Ltd.";
+ case 627:
+ return "OCEASOFT";
+ case 628:
+ return "Motsai Research";
+ case 629:
+ return "Geotab";
+ case 630:
+ return "E.G.O. Elektro-Gertebau GmbH";
+ case 631:
+ return "bewhere inc";
+ case 632:
+ return "Johnson Outdoors Inc";
+ case 633:
+ return "steute Schaltgerate GmbH & Co. KG";
+ case 634:
+ return "Ekomini inc.";
+ case 635:
+ return "DEFA AS";
+ case 636:
+ return "Aseptika Ltd";
+ case 637:
+ return "HUAWEI Technologies Co., Ltd. ( 华为技术有限公司 )";
+ case 638:
+ return "HabitAware, LLC";
+ case 639:
+ return "ruwido austria gmbh";
+ case 640:
+ return "ITEC corporation";
+ case 641:
+ return "StoneL";
+ case 642:
+ return "Sonova AG";
+ case 643:
+ return "Maven Machines, Inc.";
+ case 644:
+ return "Synapse Electronics";
+ case 645:
+ return "Standard Innovation Inc.";
+ case 646:
+ return "RF Code, Inc.";
+ case 647:
+ return "Wally Ventures S.L.";
+ case 648:
+ return "Willowbank Electronics Ltd";
+ case 649:
+ return "SK Telecom";
+ case 650:
+ return "Jetro AS";
+ case 651:
+ return "Code Gears LTD";
+ case 652:
+ return "NANOLINK APS";
+ case 653:
+ return "IF, LLC";
+ case 654:
+ return "RF Digital Corp";
+ case 655:
+ return "Church & Dwight Co., Inc";
+ case 656:
+ return "Multibit Oy";
+ case 657:
+ return "CliniCloud Inc";
+ case 658:
+ return "SwiftSensors";
case 65535:
return "internal use";
default:
diff --git a/lib/sdp.c b/lib/sdp.c
index 2107e28..155eca5 100644
--- a/lib/sdp.c
+++ b/lib/sdp.c
@@ -4906,6 +4906,7 @@
length = 0;
break;
default:
+ sdp_list_free(subseq, free);
goto fail;
}
diff --git a/lib/uuid.c b/lib/uuid.c
index fd61968..046b521 100644
--- a/lib/uuid.c
+++ b/lib/uuid.c
@@ -289,7 +289,12 @@
int bt_uuid_strcmp(const void *a, const void *b)
{
- return strcasecmp(a, b);
+ bt_uuid_t u1, u2;
+
+ bt_string_to_uuid(&u1, a);
+ bt_string_to_uuid(&u2, b);
+
+ return bt_uuid_cmp(&u1, &u2);
}
int bt_uuid_to_le(const bt_uuid_t *src, void *dst)
diff --git a/monitor/analyze.c b/monitor/analyze.c
index eea3c9e..47ae45f 100644
--- a/monitor/analyze.c
+++ b/monitor/analyze.c
@@ -309,6 +309,9 @@
case BTSNOOP_OPCODE_SCO_RX_PKT:
sco_pkt(&tv, index, buf, pktlen);
break;
+ case BTSNOOP_OPCODE_OPEN_INDEX:
+ case BTSNOOP_OPCODE_CLOSE_INDEX:
+ break;
default:
fprintf(stderr, "Wrong opcode %u\n", opcode);
goto done;
diff --git a/monitor/avctp.c b/monitor/avctp.c
index a54b051..d06640e 100644
--- a/monitor/avctp.c
+++ b/monitor/avctp.c
@@ -1818,9 +1818,9 @@
{
struct l2cap_frame *frame = &avctp_frame->l2cap_frame;
uint16_t id, charset, namelen;
- uint8_t type, status;
+ uint8_t type, status, i;
uint32_t subtype;
- uint64_t features[2];
+ uint8_t features[16];
if (!l2cap_frame_get_be16(frame, &id))
return false;
@@ -1830,26 +1830,31 @@
if (!l2cap_frame_get_u8(frame, &type))
return false;
- print_field("%*cPlayerID: 0x%04x (%s)", indent, ' ',
+ print_field("%*cPlayerType: 0x%04x (%s)", indent, ' ',
type, playertype2str(type));
if (!l2cap_frame_get_be32(frame, &subtype))
return false;
- print_field("%*cPlayerID: 0x%08x (%s)", indent, ' ',
+ print_field("%*cPlayerSubType: 0x%08x (%s)", indent, ' ',
subtype, playersubtype2str(subtype));
if (!l2cap_frame_get_u8(frame, &status))
return false;
- print_field("%*cPlayerID: 0x%02x (%s)", indent, ' ',
+ print_field("%*cPlayStatus: 0x%02x (%s)", indent, ' ',
status, playstatus2str(status));
- if (!l2cap_frame_get_be128(frame, &features[0], &features[1]))
- return false;
+ printf("%*cFeatures: 0x", indent+8, ' ');
- print_field("%*cFeatures: 0x%16" PRIx64 "%16" PRIx64, indent, ' ',
- features[1], features[0]);
+ for (i = 0; i < 16; i++) {
+ if (!l2cap_frame_get_u8(frame, &features[i]))
+ return false;
+
+ printf("%02x", features[i]);
+ }
+
+ printf("\n");
if (!l2cap_frame_get_be16(frame, &charset))
return false;
@@ -1863,7 +1868,7 @@
print_field("%*cNameLength: 0x%04x (%u)", indent, ' ',
namelen, namelen);
- printf("%*cName: ", indent, ' ');
+ printf("%*cName: ", indent+8, ' ');
for (; namelen > 0; namelen--) {
uint8_t c;
@@ -1871,6 +1876,7 @@
return false;
printf("%1c", isprint(c) ? c : '.');
}
+ printf("\n");
return true;
}
@@ -2296,7 +2302,7 @@
if (!l2cap_frame_get_be16(frame, &num))
return false;
- print_field("%*cUIDCounter: 0x%04x (%u)", indent, ' ', num, num);
+ print_field("%*cNumOfItems: 0x%04x (%u)", indent, ' ', num, num);
for (; num > 0; num--) {
uint8_t type;
diff --git a/monitor/btsnoop.c b/monitor/btsnoop.c
deleted file mode 100644
index a32335a..0000000
--- a/monitor/btsnoop.c
+++ /dev/null
@@ -1,430 +0,0 @@
-/*
- *
- * BlueZ - Bluetooth protocol stack for Linux
- *
- * Copyright (C) 2011-2014 Intel Corporation
- * Copyright (C) 2002-2010 Marcel Holtmann <marcel@holtmann.org>
- *
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <stdint.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <arpa/inet.h>
-
-#include "lib/bluetooth.h"
-#include "lib/hci.h"
-
-#include "btsnoop.h"
-
-struct btsnoop_hdr {
- uint8_t id[8]; /* Identification Pattern */
- uint32_t version; /* Version Number = 1 */
- uint32_t type; /* Datalink Type */
-} __attribute__ ((packed));
-#define BTSNOOP_HDR_SIZE (sizeof(struct btsnoop_hdr))
-
-struct btsnoop_pkt {
- uint32_t size; /* Original Length */
- uint32_t len; /* Included Length */
- uint32_t flags; /* Packet Flags */
- uint32_t drops; /* Cumulative Drops */
- uint64_t ts; /* Timestamp microseconds */
- uint8_t data[0]; /* Packet Data */
-} __attribute__ ((packed));
-#define BTSNOOP_PKT_SIZE (sizeof(struct btsnoop_pkt))
-
-static const uint8_t btsnoop_id[] = { 0x62, 0x74, 0x73, 0x6e,
- 0x6f, 0x6f, 0x70, 0x00 };
-
-static const uint32_t btsnoop_version = 1;
-static uint32_t btsnoop_type = 0;
-
-static int btsnoop_fd = -1;
-static uint16_t btsnoop_index = 0xffff;
-
-void btsnoop_create(const char *path, uint32_t type)
-{
- struct btsnoop_hdr hdr;
- ssize_t written;
-
- if (btsnoop_fd >= 0)
- return;
-
- btsnoop_fd = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC,
- S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
- if (btsnoop_fd < 0)
- return;
-
- btsnoop_type = type;
-
- memcpy(hdr.id, btsnoop_id, sizeof(btsnoop_id));
- hdr.version = htobe32(btsnoop_version);
- hdr.type = htobe32(btsnoop_type);
-
- written = write(btsnoop_fd, &hdr, BTSNOOP_HDR_SIZE);
- if (written < 0) {
- close(btsnoop_fd);
- btsnoop_fd = -1;
- return;
- }
-}
-
-void btsnoop_write(struct timeval *tv, uint32_t flags,
- const void *data, uint16_t size)
-{
- struct btsnoop_pkt pkt;
- uint64_t ts;
- ssize_t written;
-
- ts = (tv->tv_sec - 946684800ll) * 1000000ll + tv->tv_usec;
-
- pkt.size = htobe32(size);
- pkt.len = htobe32(size);
- pkt.flags = htobe32(flags);
- pkt.drops = htobe32(0);
- pkt.ts = htobe64(ts + 0x00E03AB44A676000ll);
-
- written = write(btsnoop_fd, &pkt, BTSNOOP_PKT_SIZE);
- if (written < 0)
- return;
-
- if (data && size > 0) {
- written = write(btsnoop_fd, data, size);
- if (written < 0)
- return;
- }
-}
-
-static uint32_t get_flags_from_opcode(uint16_t opcode)
-{
- switch (opcode) {
- case BTSNOOP_OPCODE_NEW_INDEX:
- case BTSNOOP_OPCODE_DEL_INDEX:
- break;
- case BTSNOOP_OPCODE_COMMAND_PKT:
- return 0x02;
- case BTSNOOP_OPCODE_EVENT_PKT:
- return 0x03;
- case BTSNOOP_OPCODE_ACL_TX_PKT:
- return 0x00;
- case BTSNOOP_OPCODE_ACL_RX_PKT:
- return 0x01;
- case BTSNOOP_OPCODE_SCO_TX_PKT:
- case BTSNOOP_OPCODE_SCO_RX_PKT:
- break;
- }
-
- return 0xff;
-}
-
-void btsnoop_write_hci(struct timeval *tv, uint16_t index, uint16_t opcode,
- const void *data, uint16_t size)
-{
- uint32_t flags;
-
- if (!tv)
- return;
-
- if (btsnoop_fd < 0)
- return;
-
- switch (btsnoop_type) {
- case BTSNOOP_TYPE_HCI:
- if (btsnoop_index == 0xffff)
- btsnoop_index = index;
-
- if (index != btsnoop_index)
- return;
-
- flags = get_flags_from_opcode(opcode);
- if (flags == 0xff)
- return;
- break;
-
- case BTSNOOP_TYPE_MONITOR:
- flags = (index << 16) | opcode;
- break;
-
- default:
- return;
- }
-
- btsnoop_write(tv, flags, data, size);
-}
-
-void btsnoop_write_phy(struct timeval *tv, uint16_t frequency,
- const void *data, uint16_t size)
-{
- uint32_t flags;
-
- if (!tv)
- return;
-
- if (btsnoop_fd < 0)
- return;
-
- switch (btsnoop_type) {
- case BTSNOOP_TYPE_SIMULATOR:
- flags = (1 << 16) | frequency;
- break;
-
- default:
- return;
- }
-
- btsnoop_write(tv, flags, data, size);
-}
-
-int btsnoop_open(const char *path, uint32_t *type)
-{
- struct btsnoop_hdr hdr;
- ssize_t len;
-
- if (btsnoop_fd >= 0) {
- fprintf(stderr, "Too many open files\n");
- return -1;
- }
-
- btsnoop_fd = open(path, O_RDONLY | O_CLOEXEC);
- if (btsnoop_fd < 0) {
- perror("Failed to open file");
- return -1;
- }
-
- len = read(btsnoop_fd, &hdr, BTSNOOP_HDR_SIZE);
- if (len < 0 || len != BTSNOOP_HDR_SIZE) {
- perror("Failed to read header");
- close(btsnoop_fd);
- btsnoop_fd = -1;
- return -1;
- }
-
- if (memcmp(hdr.id, btsnoop_id, sizeof(btsnoop_id))) {
- fprintf(stderr, "Invalid btsnoop header\n");
- close(btsnoop_fd);
- btsnoop_fd = -1;
- return -1;
- }
-
- if (be32toh(hdr.version) != btsnoop_version) {
- fprintf(stderr, "Invalid btsnoop version\n");
- close(btsnoop_fd);
- btsnoop_fd = -1;
- return -1;
- }
-
- btsnoop_type = be32toh(hdr.type);
-
- if (type)
- *type = btsnoop_type;
-
- return 0;
-}
-
-static uint16_t get_opcode_from_flags(uint8_t type, uint32_t flags)
-{
- switch (type) {
- case HCI_COMMAND_PKT:
- return BTSNOOP_OPCODE_COMMAND_PKT;
- case HCI_ACLDATA_PKT:
- if (flags & 0x01)
- return BTSNOOP_OPCODE_ACL_RX_PKT;
- else
- return BTSNOOP_OPCODE_ACL_TX_PKT;
- case HCI_SCODATA_PKT:
- if (flags & 0x01)
- return BTSNOOP_OPCODE_SCO_RX_PKT;
- else
- return BTSNOOP_OPCODE_SCO_TX_PKT;
- case HCI_EVENT_PKT:
- return BTSNOOP_OPCODE_EVENT_PKT;
- case 0xff:
- if (flags & 0x02) {
- if (flags & 0x01)
- return BTSNOOP_OPCODE_EVENT_PKT;
- else
- return BTSNOOP_OPCODE_COMMAND_PKT;
- } else {
- if (flags & 0x01)
- return BTSNOOP_OPCODE_ACL_RX_PKT;
- else
- return BTSNOOP_OPCODE_ACL_TX_PKT;
- }
- break;
- }
-
- return 0xff;
-}
-
-int btsnoop_read_hci(struct timeval *tv, uint16_t *index, uint16_t *opcode,
- void *data, uint16_t *size)
-{
- struct btsnoop_pkt pkt;
- uint32_t toread, flags;
- uint64_t ts;
- uint8_t pkt_type;
- ssize_t len;
-
- if (btsnoop_fd < 0)
- return -1;
-
- len = read(btsnoop_fd, &pkt, BTSNOOP_PKT_SIZE);
- if (len == 0)
- return -1;
-
- if (len < 0 || len != BTSNOOP_PKT_SIZE) {
- perror("Failed to read packet");
- close(btsnoop_fd);
- btsnoop_fd = -1;
- return -1;
- }
-
- toread = be32toh(pkt.size);
- if (toread > BTSNOOP_MAX_PACKET_SIZE) {
- perror("Packet len suspicially big: %u", toread);
- close(btsnoop_fd);
- btsnoop_fd = -1;
- return -1;
- }
-
- flags = be32toh(pkt.flags);
-
- ts = be64toh(pkt.ts) - 0x00E03AB44A676000ll;
- tv->tv_sec = (ts / 1000000ll) + 946684800ll;
- tv->tv_usec = ts % 1000000ll;
-
- switch (btsnoop_type) {
- case BTSNOOP_TYPE_HCI:
- *index = 0;
- *opcode = get_opcode_from_flags(0xff, flags);
- break;
-
- case BTSNOOP_TYPE_UART:
- len = read(btsnoop_fd, &pkt_type, 1);
- if (len < 0) {
- perror("Failed to read packet type");
- close(btsnoop_fd);
- btsnoop_fd = -1;
- return -1;
- }
- toread--;
-
- *index = 0;
- *opcode = get_opcode_from_flags(pkt_type, flags);
- break;
-
- case BTSNOOP_TYPE_MONITOR:
- *index = flags >> 16;
- *opcode = flags & 0xffff;
- break;
-
- default:
- fprintf(stderr, "Unknown packet type\n");
- close(btsnoop_fd);
- btsnoop_fd = -1;
- return -1;
- }
-
- len = read(btsnoop_fd, data, toread);
- if (len < 0) {
- perror("Failed to read data");
- close(btsnoop_fd);
- btsnoop_fd = -1;
- return -1;
- }
-
- *size = toread;
-
- return 0;
-}
-
-int btsnoop_read_phy(struct timeval *tv, uint16_t *frequency,
- void *data, uint16_t *size)
-{
- struct btsnoop_pkt pkt;
- uint32_t toread, flags;
- uint64_t ts;
- ssize_t len;
-
- if (btsnoop_fd < 0)
- return -1;
-
- len = read(btsnoop_fd, &pkt, BTSNOOP_PKT_SIZE);
- if (len == 0)
- return -1;
-
- if (len < 0 || len != BTSNOOP_PKT_SIZE) {
- perror("Failed to read packet");
- close(btsnoop_fd);
- btsnoop_fd = -1;
- return -1;
- }
-
- toread = be32toh(pkt.size);
- flags = be32toh(pkt.flags);
-
- ts = be64toh(pkt.ts) - 0x00E03AB44A676000ll;
- tv->tv_sec = (ts / 1000000ll) + 946684800ll;
- tv->tv_usec = ts % 1000000ll;
-
- switch (btsnoop_type) {
- case BTSNOOP_TYPE_SIMULATOR:
- if ((flags >> 16) != 1)
- break;
- *frequency = flags & 0xffff;
- break;
-
- default:
- fprintf(stderr, "Unknown packet type\n");
- close(btsnoop_fd);
- btsnoop_fd = -1;
- return -1;
- }
-
- len = read(btsnoop_fd, data, toread);
- if (len < 0) {
- perror("Failed to read data");
- close(btsnoop_fd);
- btsnoop_fd = -1;
- return -1;
- }
-
- *size = toread;
-
- return 0;
-}
-
-void btsnoop_close(void)
-{
- if (btsnoop_fd < 0)
- return;
-
- close(btsnoop_fd);
- btsnoop_fd = -1;
-
- btsnoop_index = 0xffff;
-}
diff --git a/monitor/btsnoop.h b/monitor/btsnoop.h
deleted file mode 100644
index be4e2ed..0000000
--- a/monitor/btsnoop.h
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- *
- * BlueZ - Bluetooth protocol stack for Linux
- *
- * Copyright (C) 2011-2014 Intel Corporation
- * Copyright (C) 2002-2010 Marcel Holtmann <marcel@holtmann.org>
- *
- *
- * 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 <stdint.h>
-#include <sys/time.h>
-
-#define BTSNOOP_TYPE_HCI 1001
-#define BTSNOOP_TYPE_UART 1002
-#define BTSNOOP_TYPE_BCSP 1003
-#define BTSNOOP_TYPE_3WIRE 1004
-#define BTSNOOP_TYPE_MONITOR 2001
-#define BTSNOOP_TYPE_SIMULATOR 2002
-
-#define BTSNOOP_OPCODE_NEW_INDEX 0
-#define BTSNOOP_OPCODE_DEL_INDEX 1
-#define BTSNOOP_OPCODE_COMMAND_PKT 2
-#define BTSNOOP_OPCODE_EVENT_PKT 3
-#define BTSNOOP_OPCODE_ACL_TX_PKT 4
-#define BTSNOOP_OPCODE_ACL_RX_PKT 5
-#define BTSNOOP_OPCODE_SCO_TX_PKT 6
-#define BTSNOOP_OPCODE_SCO_RX_PKT 7
-
-struct btsnoop_opcode_new_index {
- uint8_t type;
- uint8_t bus;
- uint8_t bdaddr[6];
- char name[8];
-} __attribute__((packed));
-
-void btsnoop_create(const char *path, uint32_t type);
-void btsnoop_write(struct timeval *tv, uint32_t flags,
- const void *data, uint16_t size);
-void btsnoop_write_hci(struct timeval *tv, uint16_t index, uint16_t opcode,
- const void *data, uint16_t size);
-void btsnoop_write_phy(struct timeval *tv, uint16_t frequency,
- const void *data, uint16_t size);
-int btsnoop_open(const char *path, uint32_t *type);
-int btsnoop_read_hci(struct timeval *tv, uint16_t *index, uint16_t *opcode,
- void *data, uint16_t *size);
-int btsnoop_read_phy(struct timeval *tv, uint16_t *frequency,
- void *data, uint16_t *size);
-void btsnoop_close(void);
diff --git a/monitor/packet.c b/monitor/packet.c
index d6c74e9..d40b69d 100644
--- a/monitor/packet.c
+++ b/monitor/packet.c
@@ -59,6 +59,9 @@
#define COLOR_NEW_INDEX COLOR_GREEN
#define COLOR_DEL_INDEX COLOR_RED
+#define COLOR_OPEN_INDEX COLOR_GREEN
+#define COLOR_CLOSE_INDEX COLOR_RED
+#define COLOR_INDEX_INFO COLOR_GREEN
#define COLOR_HCI_COMMAND COLOR_BLUE
#define COLOR_HCI_COMMAND_UNKNOWN COLOR_WHITE_BG
@@ -3664,8 +3667,9 @@
#define MAX_INDEX 16
struct index_data {
- uint8_t type;
- uint8_t bdaddr[6];
+ uint8_t type;
+ uint8_t bdaddr[6];
+ uint16_t manufacturer;
};
static struct index_data index_list[MAX_INDEX];
@@ -3674,7 +3678,9 @@
const void *data, uint16_t size)
{
const struct btsnoop_opcode_new_index *ni;
+ const struct btsnoop_opcode_index_info *ii;
char str[18], extra_str[24];
+ uint16_t manufacturer;
if (index_filter && index_number != index)
return;
@@ -3722,6 +3728,34 @@
case BTSNOOP_OPCODE_SCO_RX_PKT:
packet_hci_scodata(tv, index, true, data, size);
break;
+ case BTSNOOP_OPCODE_OPEN_INDEX:
+ if (index < MAX_INDEX)
+ addr2str(index_list[index].bdaddr, str);
+ else
+ sprintf(str, "00:00:00:00:00:00");
+
+ packet_open_index(tv, index, str);
+ break;
+ case BTSNOOP_OPCODE_CLOSE_INDEX:
+ if (index < MAX_INDEX)
+ addr2str(index_list[index].bdaddr, str);
+ else
+ sprintf(str, "00:00:00:00:00:00");
+
+ packet_close_index(tv, index, str);
+ break;
+ case BTSNOOP_OPCODE_INDEX_INFO:
+ ii = data;
+ manufacturer = le16_to_cpu(ii->manufacturer);
+
+ if (index < MAX_INDEX) {
+ memcpy(index_list[index].bdaddr, ii->bdaddr, 6);
+ index_list[index].manufacturer = manufacturer;
+ }
+
+ addr2str(ii->bdaddr, str);
+ packet_index_info(tv, index, str, manufacturer);
+ break;
default:
sprintf(extra_str, "(code %d len %d)", opcode, size);
print_packet(tv, index, '*', COLOR_ERROR,
@@ -8447,6 +8481,29 @@
label, NULL);
}
+void packet_open_index(struct timeval *tv, uint16_t index, const char *label)
+{
+ print_packet(tv, index, '=', COLOR_OPEN_INDEX, "Open Index",
+ label, NULL);
+}
+
+void packet_close_index(struct timeval *tv, uint16_t index, const char *label)
+{
+ print_packet(tv, index, '=', COLOR_CLOSE_INDEX, "Close Index",
+ label, NULL);
+}
+
+void packet_index_info(struct timeval *tv, uint16_t index, const char *label,
+ uint16_t manufacturer)
+{
+ char details[128];
+
+ sprintf(details, "(%s)", bt_compidtostr(manufacturer));
+
+ print_packet(tv, index, '=', COLOR_INDEX_INFO, "Index Info",
+ label, details);
+}
+
void packet_hci_command(struct timeval *tv, uint16_t index,
const void *data, uint16_t size)
{
diff --git a/monitor/packet.h b/monitor/packet.h
index c39816b..43fabc0 100644
--- a/monitor/packet.h
+++ b/monitor/packet.h
@@ -63,6 +63,10 @@
void packet_new_index(struct timeval *tv, uint16_t index, const char *label,
uint8_t type, uint8_t bus, const char *name);
void packet_del_index(struct timeval *tv, uint16_t index, const char *label);
+void packet_open_index(struct timeval *tv, uint16_t index, const char *label);
+void packet_close_index(struct timeval *tv, uint16_t index, const char *label);
+void packet_index_info(struct timeval *tv, uint16_t index, const char *label,
+ uint16_t manufacturer);
void packet_hci_command(struct timeval *tv, uint16_t index,
const void *data, uint16_t size);
diff --git a/monitor/uuid.c b/monitor/uuid.c
index 1912661..fe79f3f 100644
--- a/monitor/uuid.c
+++ b/monitor/uuid.c
@@ -334,6 +334,130 @@
{ 0x2aa1, "Magnetic Flux Density - 3D" },
{ 0x2aa2, "Language" },
{ 0x2aa3, "Barometric Pressure Trend" },
+ /* vendor defined */
+ { 0xfeff, "GN Netcom" },
+ { 0xfefe, "GN ReSound A/S" },
+ { 0xfefd, "Gimbal, Inc." },
+ { 0xfefc, "Gimbal, Inc." },
+ { 0xfefb, "Stollmann E+V GmbH" },
+ { 0xfefa, "PayPal, Inc." },
+ { 0xfef9, "PayPal, Inc." },
+ { 0xfef8, "Aplix Corporation" },
+ { 0xfef7, "Aplix Corporation" },
+ { 0xfef6, "Wicentric, Inc." },
+ { 0xfef5, "Dialog Semiconductor GmbH" },
+ { 0xfef4, "Google" },
+ { 0xfef3, "Google" },
+ { 0xfef2, "CSR" },
+ { 0xfef1, "CSR" },
+ { 0xfef0, "Intel" },
+ { 0xfeef, "Polar Electro Oy" },
+ { 0xfeee, "Polar Electro Oy" },
+ { 0xfeed, "Tile, Inc." },
+ { 0xfeec, "Tile, Inc." },
+ { 0xfeeb, "Swirl Networks, Inc." },
+ { 0xfeea, "Swirl Networks, Inc." },
+ { 0xfee9, "Quintic Corp." },
+ { 0xfee8, "Quintic Corp." },
+ { 0xfee7, "Tencent Holdings Limited" },
+ { 0xfee6, "Seed Labs, Inc." },
+ { 0xfee5, "Nordic Semiconductor ASA" },
+ { 0xfee4, "Nordic Semiconductor ASA" },
+ { 0xfee3, "Anki, Inc." },
+ { 0xfee2, "Anki, Inc." },
+ { 0xfee1, "Anhui Huami Information Technology Co." },
+ { 0xfee0, "Anhui Huami Information Technology Co." },
+ { 0xfedf, "Design SHIFT" },
+ { 0xfede, "Coin, Inc." },
+ { 0xfedd, "Jawbone" },
+ { 0xfedc, "Jawbone" },
+ { 0xfedb, "Perka, Inc." },
+ { 0xfeda, "ISSC Technologies Corporation" },
+ { 0xfed9, "Pebble Technology Corporation" },
+ { 0xfed8, "Google" },
+ { 0xfed7, "Broadcom Corporation" },
+ { 0xfed6, "Broadcom Corporation" },
+ { 0xfed5, "Plantronics Inc." },
+ { 0xfed4, "Apple, Inc." },
+ { 0xfed3, "Apple, Inc." },
+ { 0xfed2, "Apple, Inc." },
+ { 0xfed1, "Apple, Inc." },
+ { 0xfed0, "Apple, Inc." },
+ { 0xfecf, "Apple, Inc." },
+ { 0xfece, "Apple, Inc." },
+ { 0xfecd, "Apple, Inc." },
+ { 0xfecc, "Apple, Inc." },
+ { 0xfecb, "Apple, Inc." },
+ { 0xfeca, "Apple, Inc." },
+ { 0xfec9, "Apple, Inc." },
+ { 0xfec8, "Apple, Inc." },
+ { 0xfec7, "Apple, Inc." },
+ { 0xfec6, "Kocomojo, LLC" },
+ { 0xfec5, "Realtek Semiconductor Corp." },
+ { 0xfec4, "PLUS Location Systems" },
+ { 0xfec3, "360fly, Inc." },
+ { 0xfec2, "Blue Spark Technologies, Inc." },
+ { 0xfec1, "KDDI Corporation" },
+ { 0xfec0, "KDDI Corporation" },
+ { 0xfebf, "Nod, Inc." },
+ { 0xfebe, "Bose Corporation" },
+ { 0xfebd, "Clover Network, Inc." },
+ { 0xfebc, "Dexcom, Inc." },
+ { 0xfebb, "adafruit industries" },
+ { 0xfeba, "Tencent Holdings Limited" },
+ { 0xfeb9, "LG Electronics" },
+ { 0xfeb8, "Facebook, Inc." },
+ { 0xfeb7, "Facebook, Inc." },
+ { 0xfeb6, "Vencer Co, Ltd" },
+ { 0xfeb5, "WiSilica Inc." },
+ { 0xfeb4, "WiSilica Inc." },
+ { 0xfeb3, "Taobao" },
+ { 0xfeb2, "Microsoft Corporation" },
+ { 0xfeb1, "Electronics Tomorrow Limited" },
+ { 0xfeb0, "Nest Labs Inc." },
+ { 0xfeaf, "Nest Labs Inc." },
+ { 0xfeae, "Nokia Corporation" },
+ { 0xfead, "Nokia Corporation" },
+ { 0xfeac, "Nokia Corporation" },
+ { 0xfeab, "Nokia Corporation" },
+ { 0xfeaa, "Google" },
+ { 0xfea9, "Savant Systems LLC" },
+ { 0xfea8, "Savant Systems LLC" },
+ { 0xfea7, "UTC Fire and Security" },
+ { 0xfea6, "GoPro, Inc." },
+ { 0xfea5, "GoPro, Inc." },
+ { 0xfea4, "Paxton Access Ltd" },
+ { 0xfea3, "ITT Industries" },
+ { 0xfea2, "Intrepid Control Systems, Inc." },
+ { 0xfea1, "Intrepid Control Systems, Inc." },
+ { 0xfea0, "Google" },
+ { 0xfe9f, "Google" },
+ { 0xfe9e, "Dialog Semiconductor B.V." },
+ { 0xfe9d, "Mobiquity Networks Inc" },
+ { 0xfe9c, "GSI Laboratories, Inc." },
+ { 0xfe9b, "Samsara Networks, Inc" },
+ { 0xfe9a, "Estimote" },
+ { 0xfe99, "Currant, Inc." },
+ { 0xfe98, "Currant, Inc." },
+ { 0xfe97, "Tesla Motor Inc." },
+ { 0xfe96, "Tesla Motor Inc." },
+ { 0xfe95, "Xiaomi Inc." },
+ { 0xfe94, "OttoQ Inc." },
+ { 0xfe93, "OttoQ Inc." },
+ { 0xfe92, "Jarden Safety & Security" },
+ { 0xfe91, "Shanghai Imilab Technology Co.,Ltd" },
+ { 0xfe90, "JUMA" },
+ { 0xfe8f, "CSR" },
+ { 0xfe8e, "ARM Ltd" },
+ { 0xfe8d, "Interaxon Inc." },
+ { 0xfe8c, "TRON Forum" },
+ { 0xfe8b, "Apple, Inc." },
+ { 0xfe8a, "Apple, Inc." },
+ { 0xfe89, "B&O Play A/S" },
+ { 0xfe88, "SALTO SYSTEMS S.L." },
+ /* SDO defined */
+ { 0xfffe, "Alliance for Wireless Power (A4WP)" },
+ { 0xfffd, "Fast IDentity Online Alliance (FIDO)" },
{ }
};
diff --git a/obexd/client/session.c b/obexd/client/session.c
index d6da0a7..7248768 100644
--- a/obexd/client/session.c
+++ b/obexd/client/session.c
@@ -112,6 +112,7 @@
GQueue *queue;
guint process_id;
char *folder;
+ struct callback_data *callback;
};
static GSList *sessions = NULL;
@@ -303,6 +304,16 @@
session_free(session);
}
+static void callback_destroy(struct callback_data *callback, GError *err)
+{
+ struct obc_session *session = callback->session;
+
+ callback->func(session, NULL, err, callback->data);
+ g_free(callback);
+ session->callback = NULL;
+ obc_session_unref(session);
+}
+
static void connect_cb(GObex *obex, GError *err, GObexPacket *rsp,
gpointer user_data)
{
@@ -322,11 +333,9 @@
"OBEX Connect failed with 0x%02x", rsp_code);
done:
- callback->func(callback->session, NULL, gerr, callback->data);
+ callback_destroy(callback, gerr);
if (gerr != NULL)
g_error_free(gerr);
- obc_session_unref(callback->session);
- g_free(callback);
}
static void session_disconnected(GObex *obex, GError *err, gpointer user_data)
@@ -415,17 +424,28 @@
return;
done:
- callback->func(callback->session, NULL, err, callback->data);
- obc_session_unref(callback->session);
- g_free(callback);
+ callback_destroy(callback, err);
}
static void owner_disconnected(DBusConnection *connection, void *user_data)
{
struct obc_session *session = user_data;
+ GError *err;
DBG("");
+ /*
+ * If connection still connecting notify the callback to destroy the
+ * session.
+ */
+ if (session->callback) {
+ err = g_error_new(OBEX_IO_ERROR, OBEX_IO_DISCONNECTED,
+ "Session closed by user");
+ callback_destroy(session->callback, err);
+ g_error_free(err);
+ return;
+ }
+
obc_session_shutdown(session);
}
@@ -531,6 +551,8 @@
return -EINVAL;
}
+ session->callback = callback;
+
return 0;
}
diff --git a/plugins/policy.c b/plugins/policy.c
index 6bb8268..218a3ed 100644
--- a/plugins/policy.c
+++ b/plugins/policy.c
@@ -80,6 +80,8 @@
static unsigned int service_id = 0;
static GSList *devices = NULL;
+static bool auto_enable = false;
+
struct policy_data {
struct btd_device *dev;
@@ -743,6 +745,20 @@
reconnect_set_timer(reconnect);
}
+static int policy_adapter_probe(struct btd_adapter *adapter)
+{
+ DBG("");
+
+ btd_adapter_restore_powered(adapter);
+
+ return 0;
+}
+
+static struct btd_adapter_driver policy_driver = {
+ .name = "policy",
+ .probe = policy_adapter_probe,
+};
+
static int policy_init(void)
{
GError *gerr = NULL;
@@ -758,7 +774,7 @@
sizeof(*reconnect_intervals);
reconnect_intervals = g_memdup(default_intervals,
reconnect_intervals_len);
- goto add_cb;
+ goto done;
}
reconnect_uuids = g_key_file_get_string_list(conf, "Policy",
@@ -788,12 +804,18 @@
reconnect_intervals_len);
}
-add_cb:
+ auto_enable = g_key_file_get_boolean(conf, "Policy", "AutoEnable",
+ NULL);
+
+done:
if (reconnect_uuids && reconnect_uuids[0] && reconnect_attempts) {
btd_add_disconnect_cb(disconnect_cb);
btd_add_conn_fail_cb(conn_fail_cb);
}
+ if (auto_enable)
+ btd_register_adapter_driver(&policy_driver);
+
return 0;
}
@@ -812,6 +834,9 @@
g_slist_free_full(devices, policy_remove);
btd_service_remove_state_cb(service_id);
+
+ if (auto_enable)
+ btd_unregister_adapter_driver(&policy_driver);
}
BLUETOOTH_PLUGIN_DEFINE(policy, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
diff --git a/profiles/audio/avdtp.c b/profiles/audio/avdtp.c
index 37f7a59..52ee767 100644
--- a/profiles/audio/avdtp.c
+++ b/profiles/audio/avdtp.c
@@ -1710,8 +1710,7 @@
for (i = 0; i < seid_count; i++, seid++) {
failed_seid = seid->seid;
- sep = find_local_sep_by_seid(session,
- req->first_seid.seid);
+ sep = find_local_sep_by_seid(session, seid->seid);
if (!sep || !sep->stream) {
err = AVDTP_BAD_ACP_SEID;
goto failed;
@@ -1822,8 +1821,7 @@
for (i = 0; i < seid_count; i++, seid++) {
failed_seid = seid->seid;
- sep = find_local_sep_by_seid(session,
- req->first_seid.seid);
+ sep = find_local_sep_by_seid(session, seid->seid);
if (!sep || !sep->stream) {
err = AVDTP_BAD_ACP_SEID;
goto failed;
diff --git a/profiles/audio/avrcp.c b/profiles/audio/avrcp.c
index de7590a..9187692 100644
--- a/profiles/audio/avrcp.c
+++ b/profiles/audio/avrcp.c
@@ -103,6 +103,7 @@
#define AVRCP_REQUEST_CONTINUING 0x40
#define AVRCP_ABORT_CONTINUING 0x41
#define AVRCP_SET_ABSOLUTE_VOLUME 0x50
+#define AVRCP_SET_ADDRESSED_PLAYER 0x60
#define AVRCP_SET_BROWSED_PLAYER 0x70
#define AVRCP_GET_FOLDER_ITEMS 0x71
#define AVRCP_CHANGE_PATH 0x72
@@ -136,6 +137,13 @@
#define AVRCP_CHARSET_UTF8 106
#define AVRCP_BROWSING_TIMEOUT 1
+#define AVRCP_CT_VERSION 0x0106
+#define AVRCP_TG_VERSION 0x0105
+
+#define AVRCP_SCOPE_MEDIA_PLAYER_LIST 0x00
+#define AVRCP_SCOPE_MEDIA_PLAYER_VFS 0x01
+#define AVRCP_SCOPE_SEARCH 0x02
+#define AVRCP_SCOPE_NOW_PLAYING 0x03
#if __BYTE_ORDER == __LITTLE_ENDIAN
@@ -175,6 +183,30 @@
} __attribute__ ((packed));
#define AVRCP_BROWSING_HEADER_LENGTH 3
+struct get_folder_items_rsp {
+ uint8_t status;
+ uint16_t uid_counter;
+ uint16_t num_items;
+ uint8_t data[0];
+} __attribute__ ((packed));
+
+struct folder_item {
+ uint8_t type;
+ uint16_t len;
+ uint8_t data[0];
+} __attribute__ ((packed));
+
+struct player_item {
+ uint16_t player_id;
+ uint8_t type;
+ uint32_t subtype;
+ uint8_t status;
+ uint8_t features[16];
+ uint16_t charset;
+ uint16_t namelen;
+ char name[0];
+} __attribute__ ((packed));
+
struct avrcp_server {
struct btd_adapter *adapter;
uint32_t tg_record_id;
@@ -204,8 +236,10 @@
uint64_t uid;
uint16_t uid_counter;
bool browsed;
+ bool addressed;
uint8_t *features;
char *path;
+ guint changed_id;
struct pending_list_items *p;
char *change_path;
@@ -258,6 +292,13 @@
static GSList *servers = NULL;
static unsigned int avctp_id = 0;
+/* Default feature bit mask for media player as per avctp.c:key_map */
+static const uint8_t features[16] = {
+ 0xF8, 0xBF, 0xFF, 0xBF, 0x1F,
+ 0xFB, 0x3F, 0x60, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00 };
+
/* Company IDs supported by this device */
static uint32_t company_ids[] = {
IEEEID_BTSIG,
@@ -274,7 +315,7 @@
sdp_record_t *record;
sdp_data_t *psm[2], *version, *features;
uint16_t lp = AVCTP_CONTROL_PSM, ap = AVCTP_BROWSING_PSM;
- uint16_t avrcp_ver = 0x0106, avctp_ver = 0x0103;
+ uint16_t avctp_ver = 0x0103;
uint16_t feat = ( AVRCP_FEATURE_CATEGORY_1 |
AVRCP_FEATURE_CATEGORY_2 |
AVRCP_FEATURE_CATEGORY_3 |
@@ -329,7 +370,7 @@
/* Bluetooth Profile Descriptor List */
sdp_uuid16_create(&profile[0].uuid, AV_REMOTE_PROFILE_ID);
- profile[0].version = avrcp_ver;
+ profile[0].version = AVRCP_CT_VERSION;
pfseq = sdp_list_append(NULL, &profile[0]);
sdp_set_profile_descs(record, pfseq);
@@ -367,7 +408,7 @@
sdp_list_t *aproto_browsing, *proto_browsing[2] = {0};
uint16_t lp = AVCTP_CONTROL_PSM;
uint16_t lp_browsing = AVCTP_BROWSING_PSM;
- uint16_t avrcp_ver = 0x0104, avctp_ver = 0x0103;
+ uint16_t avctp_ver = 0x0103;
uint16_t feat = ( AVRCP_FEATURE_CATEGORY_1 |
AVRCP_FEATURE_CATEGORY_2 |
AVRCP_FEATURE_CATEGORY_3 |
@@ -417,7 +458,7 @@
/* Bluetooth Profile Descriptor List */
sdp_uuid16_create(&profile[0].uuid, AV_REMOTE_PROFILE_ID);
- profile[0].version = avrcp_ver;
+ profile[0].version = AVRCP_TG_VERSION;
pfseq = sdp_list_append(NULL, &profile[0]);
sdp_set_profile_descs(record, pfseq);
@@ -627,6 +668,7 @@
{
uint8_t buf[AVRCP_HEADER_LENGTH + 9];
struct avrcp_header *pdu = (void *) buf;
+ uint8_t code;
uint16_t size;
GSList *l;
int attr;
@@ -640,10 +682,19 @@
set_company_id(pdu->company_id, IEEEID_BTSIG);
pdu->pdu_id = AVRCP_REGISTER_NOTIFICATION;
- pdu->params[0] = id;
DBG("id=%u", id);
+ if (id != AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED && player->changed_id) {
+ code = AVC_CTYPE_REJECTED;
+ size = 1;
+ pdu->params[0] = AVRCP_STATUS_ADDRESSED_PLAYER_CHANGED;
+ goto done;
+ }
+
+ code = AVC_CTYPE_CHANGED;
+ pdu->params[0] = id;
+
switch (id) {
case AVRCP_EVENT_STATUS_CHANGED:
size = 2;
@@ -674,6 +725,11 @@
pdu->params[size++] = attr;
pdu->params[size++] = val;
break;
+ case AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED:
+ size = 5;
+ memcpy(&pdu->params[1], &player->id, sizeof(uint16_t));
+ memcpy(&pdu->params[3], &player->uid_counter, sizeof(uint16_t));
+ break;
case AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED:
size = 1;
break;
@@ -682,6 +738,7 @@
return;
}
+done:
pdu->params_len = htons(size);
for (l = player->sessions; l; l = l->next) {
@@ -693,8 +750,9 @@
err = avctp_send_vendordep(session->conn,
session->transaction_events[id],
- AVC_CTYPE_CHANGED, AVC_SUBUNIT_PANEL,
+ code, AVC_SUBUNIT_PANEL,
buf, size + AVRCP_HEADER_LENGTH);
+
if (err < 0)
continue;
@@ -1306,6 +1364,22 @@
return play_status_to_val(value);
}
+static uint16_t player_get_id(struct avrcp_player *player)
+{
+ if (player == NULL)
+ return 0x0000;
+
+ return player->id;
+}
+
+static uint16_t player_get_uid_counter(struct avrcp_player *player)
+{
+ if (player == NULL)
+ return 0x0000;
+
+ return player->uid_counter;
+}
+
static uint8_t avrcp_handle_get_play_status(struct avrcp *session,
struct avrcp_header *pdu,
uint8_t transaction)
@@ -1493,6 +1567,13 @@
pdu->params[len++] = val;
}
+ g_list_free(settings);
+
+ break;
+ case AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED:
+ len = 5;
+ bt_put_be16(player_get_id(player), &pdu->params[1]);
+ bt_put_be16(player_get_uid_counter(player), &pdu->params[3]);
break;
case AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED:
len = 1;
@@ -1595,16 +1676,12 @@
struct avrcp_header *pdu,
uint8_t transaction)
{
- struct avrcp_player *player = session->controller->player;
uint16_t len = ntohs(pdu->params_len);
uint8_t volume;
if (len != 1)
goto err;
- if (!player)
- goto err;
-
volume = pdu->params[0] & 0x7F;
media_transport_update_device_volume(session->dev, volume);
@@ -1617,6 +1694,90 @@
return AVC_CTYPE_REJECTED;
}
+static struct avrcp_player *find_tg_player(struct avrcp *session, uint16_t id)
+{
+ struct avrcp_server *server = session->server;
+ GSList *l;
+
+ for (l = server->players; l; l = l->next) {
+ struct avrcp_player *player = l->data;
+
+ if (player->id == id)
+ return player;
+ }
+
+ return NULL;
+}
+
+static gboolean notify_addressed_player_changed(gpointer user_data)
+{
+ struct avrcp_player *player = user_data;
+ uint8_t events[6] = { AVRCP_EVENT_STATUS_CHANGED,
+ AVRCP_EVENT_TRACK_CHANGED,
+ AVRCP_EVENT_TRACK_REACHED_START,
+ AVRCP_EVENT_TRACK_REACHED_END,
+ AVRCP_EVENT_SETTINGS_CHANGED,
+ AVRCP_EVENT_PLAYBACK_POS_CHANGED
+ };
+ uint8_t i;
+
+ avrcp_player_event(player, AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED, NULL);
+
+ /*
+ * TG shall complete all player specific
+ * notifications with AV/C C-Type REJECTED
+ * with error code as Addressed Player Changed.
+ */
+ for (i = 0; i < sizeof(events); i++)
+ avrcp_player_event(player, events[i], NULL);
+
+ player->changed_id = 0;
+
+ return FALSE;
+}
+
+static uint8_t avrcp_handle_set_addressed_player(struct avrcp *session,
+ struct avrcp_header *pdu,
+ uint8_t transaction)
+{
+ struct avrcp_player *player;
+ uint16_t len = ntohs(pdu->params_len);
+ uint16_t player_id = 0;
+ uint8_t status;
+
+ if (len < 1) {
+ status = AVRCP_STATUS_INVALID_PARAM;
+ goto err;
+ }
+
+ player_id = bt_get_be16(&pdu->params[0]);
+ player = find_tg_player(session, player_id);
+ pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE;
+
+ if (player) {
+ player->addressed = true;
+ status = AVRCP_STATUS_SUCCESS;
+ pdu->params_len = htons(len);
+ pdu->params[0] = status;
+ } else {
+ status = AVRCP_STATUS_INVALID_PLAYER_ID;
+ goto err;
+ }
+
+ /* Don't emit player changed immediately since PTS expect the
+ * response of SetAddressedPlayer before the event.
+ */
+ player->changed_id = g_idle_add(notify_addressed_player_changed,
+ player);
+
+ return AVC_CTYPE_ACCEPTED;
+
+err:
+ pdu->params_len = htons(sizeof(status));
+ pdu->params[0] = status;
+ return AVC_CTYPE_REJECTED;
+}
+
static const struct control_pdu_handler control_handlers[] = {
{ AVRCP_GET_CAPABILITIES, AVC_CTYPE_STATUS,
avrcp_handle_get_capabilities },
@@ -1648,6 +1809,8 @@
avrcp_handle_request_continuing },
{ AVRCP_ABORT_CONTINUING, AVC_CTYPE_CONTROL,
avrcp_handle_abort_continuing },
+ { AVRCP_SET_ADDRESSED_PLAYER, AVC_CTYPE_CONTROL,
+ avrcp_handle_set_addressed_player },
{ },
};
@@ -1710,11 +1873,122 @@
return AVRCP_HEADER_LENGTH + 1;
}
+static void avrcp_handle_media_player_list(struct avrcp *session,
+ struct avrcp_browsing_header *pdu,
+ uint32_t start_item, uint32_t end_item)
+{
+ struct avrcp_player *player = session->target->player;
+ struct get_folder_items_rsp *rsp;
+ const char *name = NULL;
+ GSList *l;
+
+ rsp = (void *)pdu->params;
+ rsp->status = AVRCP_STATUS_SUCCESS;
+ rsp->uid_counter = htons(player_get_uid_counter(player));
+ rsp->num_items = 0;
+ pdu->param_len = sizeof(*rsp);
+
+ for (l = g_slist_nth(session->server->players, start_item);
+ l; l = g_slist_next(l)) {
+ struct avrcp_player *player = l->data;
+ struct folder_item *folder;
+ struct player_item *item;
+ uint16_t namelen;
+
+ if (rsp->num_items == (end_item - start_item) + 1)
+ break;
+
+ folder = (void *)&pdu->params[pdu->param_len];
+ folder->type = 0x01; /* Media Player */
+
+ pdu->param_len += sizeof(*folder);
+
+ item = (void *)folder->data;
+ item->player_id = htons(player->id);
+ item->type = 0x01; /* Audio */
+ item->subtype = htonl(0x01); /* Audio Book */
+ item->status = player_get_status(player);
+ /* Assign Default Feature Bit Mask */
+ memcpy(&item->features, &features, sizeof(features));
+
+ item->charset = htons(AVRCP_CHARSET_UTF8);
+
+ name = player->cb->get_name(player->user_data);
+ namelen = strlen(name);
+ item->namelen = htons(namelen);
+ memcpy(item->name, name, namelen);
+
+ folder->len = htons(sizeof(*item) + namelen);
+ pdu->param_len += sizeof(*item) + namelen;
+ rsp->num_items++;
+ }
+
+ /* If no player could be found respond with an error */
+ if (!rsp->num_items)
+ goto failed;
+
+ rsp->num_items = htons(rsp->num_items);
+ pdu->param_len = htons(pdu->param_len);
+
+ return;
+
+failed:
+ pdu->params[0] = AVRCP_STATUS_OUT_OF_BOUNDS;
+ pdu->param_len = htons(1);
+}
+
+static void avrcp_handle_get_folder_items(struct avrcp *session,
+ struct avrcp_browsing_header *pdu,
+ uint8_t transaction)
+{
+ uint32_t start_item = 0;
+ uint32_t end_item = 0;
+ uint8_t scope;
+ uint8_t status = AVRCP_STATUS_SUCCESS;
+
+ if (ntohs(pdu->param_len) < 10) {
+ status = AVRCP_STATUS_INVALID_PARAM;
+ goto failed;
+ }
+
+ scope = pdu->params[0];
+ start_item = bt_get_be32(&pdu->params[1]);
+ end_item = bt_get_be32(&pdu->params[5]);
+
+ DBG("scope 0x%02x start_item 0x%08x end_item 0x%08x", scope,
+ start_item, end_item);
+
+ if (end_item < start_item) {
+ status = AVRCP_STATUS_INVALID_PARAM;
+ goto failed;
+ }
+
+ switch (scope) {
+ case AVRCP_SCOPE_MEDIA_PLAYER_LIST:
+ avrcp_handle_media_player_list(session, pdu,
+ start_item, end_item);
+ break;
+ case AVRCP_SCOPE_MEDIA_PLAYER_VFS:
+ case AVRCP_SCOPE_SEARCH:
+ case AVRCP_SCOPE_NOW_PLAYING:
+ default:
+ status = AVRCP_STATUS_INVALID_PARAM;
+ goto failed;
+ }
+
+ return;
+
+failed:
+ pdu->params[0] = status;
+ pdu->param_len = htons(1);
+}
+
static struct browsing_pdu_handler {
uint8_t pdu_id;
void (*func) (struct avrcp *session, struct avrcp_browsing_header *pdu,
uint8_t transaction);
} browsing_handlers[] = {
+ { AVRCP_GET_FOLDER_ITEMS, avrcp_handle_get_folder_items },
{ },
};
@@ -2917,6 +3191,15 @@
.total_items = ct_get_total_numberofitems,
};
+static void set_ct_player(struct avrcp *session, struct avrcp_player *player)
+{
+ struct btd_service *service;
+
+ session->controller->player = player;
+ service = btd_device_get_service(session->dev, AVRCP_TARGET_UUID);
+ control_set_player(service, media_player_get_path(player->user_data));
+}
+
static struct avrcp_player *create_ct_player(struct avrcp *session,
uint16_t id)
{
@@ -2938,7 +3221,7 @@
player->destroy = (GDestroyNotify) media_player_destroy;
if (session->controller->player == NULL)
- session->controller->player = player;
+ set_ct_player(session, player);
session->controller->players = g_slist_prepend(
session->controller->players,
@@ -3029,6 +3312,9 @@
if (player->destroy)
player->destroy(player->user_data);
+ if (player->changed_id > 0)
+ g_source_remove(player->changed_id);
+
g_slist_free(player->sessions);
g_free(player->path);
g_free(player->change_path);
@@ -3043,10 +3329,15 @@
for (l = player->sessions; l; l = l->next) {
struct avrcp *session = l->data;
+ struct avrcp_data *controller = session->controller;
- session->controller->players = g_slist_remove(
- session->controller->players,
- player);
+ controller->players = g_slist_remove(controller->players,
+ player);
+
+ /* Check if current player is being removed */
+ if (controller->player == player)
+ set_ct_player(session, g_slist_nth_data(
+ controller->players, 0));
}
player_destroy(player);
@@ -3097,9 +3388,6 @@
i += len;
}
- if (g_slist_find(removed, session->controller->player))
- session->controller->player = NULL;
-
g_slist_free_full(removed, player_remove);
return FALSE;
@@ -3113,6 +3401,8 @@
memset(buf, 0, sizeof(buf));
pdu->pdu_id = AVRCP_GET_FOLDER_ITEMS;
+ put_be32(0, &pdu->params[1]);
+ put_be32(UINT32_MAX, &pdu->params[5]);
pdu->param_len = htons(10);
avctp_send_browsing_req(session->conn, buf, sizeof(buf),
@@ -3222,7 +3512,7 @@
}
player->uid_counter = get_be16(&pdu->params[3]);
- session->controller->player = player;
+ set_ct_player(session, player);
if (player->features != NULL)
return;
@@ -3521,7 +3811,9 @@
return;
session->supported_events |=
- (1 << AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED);
+ (1 << AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED) |
+ (1 << AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED) |
+ (1 << AVRCP_EVENT_VOLUME_CHANGED);
/* Only check capabilities if controller is not supported */
if (session->controller == NULL)
@@ -3547,9 +3839,6 @@
DBG("%p version 0x%04x", controller, controller->version);
- if (controller->version >= 0x0104)
- session->supported_events |= (1 << AVRCP_EVENT_VOLUME_CHANGED);
-
service = btd_device_get_service(session->dev, AVRCP_TARGET_UUID);
btd_service_connecting_complete(service, 0);
@@ -3774,12 +4063,14 @@
struct avrcp_server *server;
struct avrcp_player *player;
GSList *l;
+ static uint16_t id = 0;
server = find_server(servers, adapter);
if (!server)
return NULL;
player = g_new0(struct avrcp_player, 1);
+ player->id = ++id;
player->server = server;
player->cb = cb;
player->user_data = user_data;
@@ -3827,9 +4118,10 @@
target->player = g_slist_nth_data(server->players, 0);
}
- player_destroy(player);
avrcp_player_event(player,
AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED, NULL);
+
+ player_destroy(player);
}
static gboolean avrcp_handle_set_volume(struct avctp *conn,
@@ -3854,7 +4146,53 @@
return FALSE;
}
-int avrcp_set_volume(struct btd_device *dev, uint8_t volume)
+static int avrcp_event(struct avrcp *session, uint8_t id, const void *data)
+{
+ uint8_t buf[AVRCP_HEADER_LENGTH + 2];
+ struct avrcp_header *pdu = (void *) buf;
+ uint8_t code;
+ uint16_t size;
+ int err;
+
+ /* Verify that the event is registered */
+ if (!(session->registered_events & (1 << id)))
+ return -ENOENT;
+
+ memset(buf, 0, sizeof(buf));
+
+ set_company_id(pdu->company_id, IEEEID_BTSIG);
+ pdu->pdu_id = AVRCP_REGISTER_NOTIFICATION;
+ code = AVC_CTYPE_CHANGED;
+ pdu->params[0] = id;
+
+ DBG("id=%u", id);
+
+ switch (id) {
+ case AVRCP_EVENT_VOLUME_CHANGED:
+ size = 2;
+ memcpy(&pdu->params[1], data, sizeof(uint8_t));
+ break;
+ default:
+ error("Unknown event %u", id);
+ return -EINVAL;
+ }
+
+ pdu->params_len = htons(size);
+
+ err = avctp_send_vendordep(session->conn,
+ session->transaction_events[id],
+ code, AVC_SUBUNIT_PANEL,
+ buf, size + AVRCP_HEADER_LENGTH);
+ if (err < 0)
+ return err;
+
+ /* Unregister event as per AVRCP 1.3 spec, section 5.4.2 */
+ session->registered_events ^= 1 << id;
+
+ return err;
+}
+
+int avrcp_set_volume(struct btd_device *dev, uint8_t volume, bool notify)
{
struct avrcp_server *server;
struct avrcp *session;
@@ -3866,18 +4204,23 @@
return -EINVAL;
session = find_session(server->sessions, dev);
- if (session == NULL || session->target == NULL)
+ if (session == NULL)
return -ENOTCONN;
- if (session->target->version < 0x0104)
+ if (notify) {
+ if (!session->target)
+ return -ENOTSUP;
+ return avrcp_event(session, AVRCP_EVENT_VOLUME_CHANGED,
+ &volume);
+ }
+
+ if (!session->controller || session->controller->version < 0x0104)
return -ENOTSUP;
memset(buf, 0, sizeof(buf));
set_company_id(pdu->company_id, IEEEID_BTSIG);
- DBG("volume=%u", volume);
-
pdu->pdu_id = AVRCP_SET_ABSOLUTE_VOLUME;
pdu->params[0] = volume;
pdu->params_len = htons(1);
diff --git a/profiles/audio/avrcp.h b/profiles/audio/avrcp.h
index a9aeb1a..86d310c 100644
--- a/profiles/audio/avrcp.h
+++ b/profiles/audio/avrcp.h
@@ -93,6 +93,7 @@
const char *(*get_status) (void *user_data);
uint32_t (*get_position) (void *user_data);
uint32_t (*get_duration) (void *user_data);
+ const char *(*get_name) (void *user_data);
void (*set_volume) (uint8_t volume, struct btd_device *dev,
void *user_data);
bool (*play) (void *user_data);
@@ -102,7 +103,7 @@
bool (*previous) (void *user_data);
};
-int avrcp_set_volume(struct btd_device *dev, uint8_t volume);
+int avrcp_set_volume(struct btd_device *dev, uint8_t volume, bool notify);
struct avrcp_player *avrcp_register_player(struct btd_adapter *adapter,
struct avrcp_player_cb *cb,
diff --git a/profiles/audio/control.c b/profiles/audio/control.c
index f4656d8..edc4a98 100644
--- a/profiles/audio/control.c
+++ b/profiles/audio/control.c
@@ -59,6 +59,7 @@
#include "avctp.h"
#include "control.h"
+#include "player.h"
static GSList *devices = NULL;
@@ -68,6 +69,7 @@
struct btd_service *target;
struct btd_service *remote;
unsigned int avctp_id;
+ const char *player;
};
static void state_changed(struct btd_device *dev, avctp_state_t old_state,
@@ -81,9 +83,12 @@
switch (new_state) {
case AVCTP_STATE_DISCONNECTED:
control->session = NULL;
+ control->player = NULL;
g_dbus_emit_property_changed(conn, path,
AUDIO_CONTROL_INTERFACE, "Connected");
+ g_dbus_emit_property_changed(conn, path,
+ AUDIO_CONTROL_INTERFACE, "Player");
break;
case AVCTP_STATE_CONNECTING:
@@ -215,21 +220,46 @@
return TRUE;
}
+static gboolean control_player_exists(const GDBusPropertyTable *property,
+ void *data)
+{
+ struct control *control = data;
+
+ return control->player != NULL;
+}
+
+static gboolean control_get_player(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct control *control = data;
+
+ if (!control->player)
+ return FALSE;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH,
+ &control->player);
+
+ return TRUE;
+}
+
static const GDBusMethodTable control_methods[] = {
- { GDBUS_METHOD("Play", NULL, NULL, control_play) },
- { GDBUS_METHOD("Pause", NULL, NULL, control_pause) },
- { GDBUS_METHOD("Stop", NULL, NULL, control_stop) },
- { GDBUS_METHOD("Next", NULL, NULL, control_next) },
- { GDBUS_METHOD("Previous", NULL, NULL, control_previous) },
- { GDBUS_METHOD("VolumeUp", NULL, NULL, control_volume_up) },
- { GDBUS_METHOD("VolumeDown", NULL, NULL, control_volume_down) },
- { GDBUS_METHOD("FastForward", NULL, NULL, control_fast_forward) },
- { GDBUS_METHOD("Rewind", NULL, NULL, control_rewind) },
+ { GDBUS_DEPRECATED_METHOD("Play", NULL, NULL, control_play) },
+ { GDBUS_DEPRECATED_METHOD("Pause", NULL, NULL, control_pause) },
+ { GDBUS_DEPRECATED_METHOD("Stop", NULL, NULL, control_stop) },
+ { GDBUS_DEPRECATED_METHOD("Next", NULL, NULL, control_next) },
+ { GDBUS_DEPRECATED_METHOD("Previous", NULL, NULL, control_previous) },
+ { GDBUS_DEPRECATED_METHOD("VolumeUp", NULL, NULL, control_volume_up) },
+ { GDBUS_DEPRECATED_METHOD("VolumeDown", NULL, NULL,
+ control_volume_down) },
+ { GDBUS_DEPRECATED_METHOD("FastForward", NULL, NULL,
+ control_fast_forward) },
+ { GDBUS_DEPRECATED_METHOD("Rewind", NULL, NULL, control_rewind) },
{ }
};
static const GDBusPropertyTable control_properties[] = {
{ "Connected", "b", control_property_get_connected },
+ { "Player", "o", control_get_player, NULL, control_player_exists },
{ }
};
@@ -338,3 +368,22 @@
return 0;
}
+
+int control_set_player(struct btd_service *service, const char *path)
+{
+ struct control *control = btd_service_get_user_data(service);
+
+ if (!control->session)
+ return -ENOTCONN;
+
+ if (g_strcmp0(control->player, path) == 0)
+ return -EALREADY;
+
+ control->player = path;
+
+ g_dbus_emit_property_changed(btd_get_dbus_connection(),
+ device_get_path(control->dev),
+ AUDIO_CONTROL_INTERFACE, "Player");
+
+ return 0;
+}
diff --git a/profiles/audio/control.h b/profiles/audio/control.h
index 4bda896..aab2631 100644
--- a/profiles/audio/control.h
+++ b/profiles/audio/control.h
@@ -32,3 +32,5 @@
int control_connect(struct btd_service *service);
int control_disconnect(struct btd_service *service);
+
+int control_set_player(struct btd_service *service, const char *path);
diff --git a/profiles/audio/media.c b/profiles/audio/media.c
index ed441d0..69070bf 100644
--- a/profiles/audio/media.c
+++ b/profiles/audio/media.c
@@ -112,6 +112,7 @@
bool next;
bool previous;
bool control;
+ char *name;
};
static GSList *adapters = NULL;
@@ -534,7 +535,7 @@
struct a2dp_config_data *data = user_data;
gboolean *ret_value = ret;
- data->cb(data->setup, *ret_value ? TRUE : FALSE);
+ data->cb(data->setup, ret_value ? *ret_value : FALSE);
}
static int set_config(struct a2dp_sep *sep, uint8_t *configuration,
@@ -964,6 +965,7 @@
g_free(mp->sender);
g_free(mp->path);
g_free(mp->status);
+ g_free(mp->name);
g_free(mp);
}
@@ -1012,6 +1014,13 @@
return g_hash_table_lookup(mp->settings, key);
}
+static const char *get_player_name(void *user_data)
+{
+ struct media_player *mp = user_data;
+
+ return mp->name;
+}
+
static void set_shuffle_setting(DBusMessageIter *iter, const char *value)
{
const char *key = "Shuffle";
@@ -1272,6 +1281,7 @@
.get_position = get_position,
.get_duration = get_duration,
.get_status = get_status,
+ .get_name = get_player_name,
.set_volume = set_volume,
.play = play,
.stop = stop,
@@ -1607,6 +1617,25 @@
return TRUE;
}
+static gboolean set_name(struct media_player *mp, DBusMessageIter *iter)
+{
+ const char *value;
+
+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING)
+ return FALSE;
+
+ dbus_message_iter_get_basic(iter, &value);
+
+ if (g_strcmp0(mp->name, value) == 0)
+ return TRUE;
+
+ g_free(mp->name);
+
+ mp->name = g_strdup(value);
+
+ return TRUE;
+}
+
static gboolean set_player_property(struct media_player *mp, const char *key,
DBusMessageIter *entry)
{
@@ -1647,6 +1676,9 @@
if (strcasecmp(key, "CanControl") == 0)
return set_flag(mp, &var, &mp->control);
+ if (strcasecmp(key, "Identity") == 0)
+ return set_name(mp, &var);
+
DBG("%s not supported, ignoring", key);
return TRUE;
diff --git a/profiles/audio/player.c b/profiles/audio/player.c
index 147bcbf..4736396 100644
--- a/profiles/audio/player.c
+++ b/profiles/audio/player.c
@@ -1193,6 +1193,11 @@
return mp;
}
+const char *media_player_get_path(struct media_player *mp)
+{
+ return mp->path;
+}
+
void media_player_set_duration(struct media_player *mp, uint32_t duration)
{
char *value, *curval;
diff --git a/profiles/audio/player.h b/profiles/audio/player.h
index 0871904..4ad8bfe 100644
--- a/profiles/audio/player.h
+++ b/profiles/audio/player.h
@@ -70,6 +70,7 @@
struct media_player *media_player_controller_create(const char *path,
uint16_t id);
+const char *media_player_get_path(struct media_player *mp);
void media_player_destroy(struct media_player *mp);
void media_player_set_duration(struct media_player *mp, uint32_t duration);
void media_player_set_position(struct media_player *mp, uint32_t position);
diff --git a/profiles/audio/sink.c b/profiles/audio/sink.c
index 1c36735..ac7237b 100644
--- a/profiles/audio/sink.c
+++ b/profiles/audio/sink.c
@@ -227,6 +227,8 @@
struct sink *sink = user_data;
int id, perr;
+ sink->connect_id = 0;
+
if (err) {
avdtp_unref(sink->session);
sink->session = NULL;
@@ -408,7 +410,7 @@
if (sink->connect_id > 0) {
a2dp_cancel(sink->connect_id);
sink->connect_id = 0;
- btd_service_connecting_complete(sink->service, -ECANCELED);
+ btd_service_disconnecting_complete(sink->service, 0);
avdtp_unref(sink->session);
sink->session = NULL;
diff --git a/profiles/audio/source.c b/profiles/audio/source.c
index 16a8287..372b132 100644
--- a/profiles/audio/source.c
+++ b/profiles/audio/source.c
@@ -227,6 +227,8 @@
struct source *source = user_data;
int id, perr;
+ source->connect_id = 0;
+
if (err) {
avdtp_unref(source->session);
source->session = NULL;
@@ -400,7 +402,7 @@
if (source->connect_id > 0) {
a2dp_cancel(source->connect_id);
source->connect_id = 0;
- btd_service_connecting_complete(source->service, -ECANCELED);
+ btd_service_disconnecting_complete(source->service, 0);
avdtp_unref(source->session);
source->session = NULL;
diff --git a/profiles/audio/transport.c b/profiles/audio/transport.c
index 112ec17..4121e52 100644
--- a/profiles/audio/transport.c
+++ b/profiles/audio/transport.c
@@ -653,6 +653,7 @@
struct media_transport *transport = data;
struct a2dp_transport *a2dp = transport->data;
uint16_t volume;
+ bool notify;
if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT16) {
g_dbus_pending_property_error(id,
@@ -670,12 +671,21 @@
return;
}
- if (a2dp->volume != volume)
- avrcp_set_volume(transport->device, volume);
+ g_dbus_pending_property_success(id);
+
+ if (a2dp->volume == volume)
+ return;
a2dp->volume = volume;
- g_dbus_pending_property_success(id);
+ notify = transport->source_watch ? true : false;
+ if (notify)
+ g_dbus_emit_property_changed(btd_get_dbus_connection(),
+ transport->path,
+ MEDIA_TRANSPORT_INTERFACE,
+ "Volume");
+
+ avrcp_set_volume(transport->device, volume, notify);
}
static const GDBusMethodTable transport_methods[] = {
@@ -817,7 +827,6 @@
transport->destroy = destroy_a2dp;
a2dp->volume = 127;
- avrcp_set_volume(transport->device, a2dp->volume);
transport->source_watch = source_add_state_cb(service,
source_state_changed,
transport);
diff --git a/profiles/input/hog.c b/profiles/input/hog.c
index 4be9fd2..e006add 100644
--- a/profiles/input/hog.c
+++ b/profiles/input/hog.c
@@ -633,6 +633,8 @@
int i, err;
GSList *l;
+ DBG("HoG inspecting report map");
+
if (status != 0) {
error("Report Map read failed: %s", att_ecode2str(status));
return;
@@ -703,6 +705,7 @@
bt_uhid_register(hogdev->uhid, UHID_GET_REPORT, get_report, hogdev);
hogdev->uhid_created = TRUE;
+ DBG("HoG created uHID device");
for (l = hogdev->reports; l; l = l->next) {
struct report *r = l->data;
@@ -780,6 +783,8 @@
GSList *l;
uint16_t info_handle = 0, proto_mode_handle = 0;
+ DBG("HoG inspecting characteristics");
+
if (status != 0) {
const char *str = att_ecode2str(status);
DBG("Discover all characteristics failed: %s", str);
@@ -816,6 +821,7 @@
report);
discover_descriptor(hogdev->attrib, start, end, report);
} else if (bt_uuid_cmp(&uuid, &report_map_uuid) == 0) {
+ DBG("HoG discovering report map");
gatt_read_char(hogdev->attrib, chr->value_handle,
report_map_read_cb, hogdev);
discover_descriptor(hogdev->attrib, start, end, hogdev);
@@ -838,6 +844,18 @@
hogdev);
}
+static void report_free(void *data)
+{
+ struct report *report = data;
+ struct hog_device *hogdev = report->hogdev;
+
+ if (hogdev->attrib)
+ g_attrib_unregister(hogdev->attrib, report->notifyid);
+
+ g_free(report->decl);
+ g_free(report);
+}
+
static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
{
struct hog_device *hogdev = user_data;
@@ -846,9 +864,16 @@
DBG("HoG connected");
+ if (!hogdev->uhid_created && hogdev->reports) {
+ DBG("HoG init failed previously, preparing for re-init");
+ g_slist_free_full(hogdev->reports, report_free);
+ hogdev->reports = NULL;
+ }
+
hogdev->attrib = g_attrib_ref(attrib);
if (hogdev->reports == NULL) {
+ DBG("HoG discovering characteristics");
gatt_discover_char(hogdev->attrib, prim->range.start,
prim->range.end, NULL,
char_discovered_cb, hogdev);
@@ -894,18 +919,6 @@
return hogdev;
}
-static void report_free(void *data)
-{
- struct report *report = data;
- struct hog_device *hogdev = report->hogdev;
-
- if (hogdev->attrib)
- g_attrib_unregister(hogdev->attrib, report->notifyid);
-
- g_free(report->decl);
- g_free(report);
-}
-
static void hog_free_device(struct hog_device *hogdev)
{
btd_device_unref(hogdev->device);
diff --git a/src/adapter.c b/src/adapter.c
index 47e901b..d3c515e 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -1722,7 +1722,7 @@
next = g_slist_next(l);
- if (device_is_temporary(dev))
+ if (device_is_temporary(dev) && !btd_device_is_connected(dev))
btd_adapter_remove_device(adapter, dev);
}
@@ -3474,6 +3474,13 @@
return addr_type;
}
+static void probe_devices(void *user_data)
+{
+ struct btd_device *device = user_data;
+
+ device_probe_profiles(device, btd_device_get_uuids(device));
+}
+
static void load_devices(struct btd_adapter *adapter)
{
char dirname[PATH_MAX];
@@ -3482,6 +3489,7 @@
GSList *ltks = NULL;
GSList *irks = NULL;
GSList *params = NULL;
+ GSList *added_devices = NULL;
DIR *dir;
struct dirent *entry;
@@ -3551,9 +3559,7 @@
/* TODO: register services from pre-loaded list of primaries */
- list = btd_device_get_uuids(device);
- if (list)
- device_probe_profiles(device, list);
+ added_devices = g_slist_append(added_devices, device);
device_exist:
if (key_info) {
@@ -3581,6 +3587,8 @@
g_slist_free_full(irks, g_free);
load_conn_params(adapter, params);
g_slist_free_full(params, g_free);
+
+ g_slist_free_full(added_devices, probe_devices);
}
int btd_adapter_block_address(struct btd_adapter *adapter,
diff --git a/src/device.c b/src/device.c
index 9aebb0f..3d23b34 100644
--- a/src/device.c
+++ b/src/device.c
@@ -87,6 +87,11 @@
#define RSSI_THRESHOLD 2
+#define GATT_PRIM_SVC_UUID_STR "2800"
+#define GATT_SND_SVC_UUID_STR "2801"
+#define GATT_INCLUDE_UUID_STR "2802"
+#define GATT_CHARAC_UUID_STR "2803"
+
static DBusConnection *dbus_conn = NULL;
static unsigned service_state_cb_id;
@@ -1962,6 +1967,153 @@
g_key_file_free(key_file);
}
+struct gatt_saver {
+ struct btd_device *device;
+ GKeyFile *key_file;
+};
+
+static void store_desc(struct gatt_db_attribute *attr, void *user_data)
+{
+ struct gatt_saver *saver = user_data;
+ GKeyFile *key_file = saver->key_file;
+ char handle[6], value[100], uuid_str[MAX_LEN_UUID_STR];
+ const bt_uuid_t *uuid;
+ uint16_t handle_num;
+
+ handle_num = gatt_db_attribute_get_handle(attr);
+ sprintf(handle, "%04hx", handle_num);
+
+ uuid = gatt_db_attribute_get_type(attr);
+ bt_uuid_to_string(uuid, uuid_str, sizeof(uuid_str));
+ sprintf(value, "%s", uuid_str);
+ g_key_file_set_string(key_file, "Attributes", handle, value);
+}
+
+static void store_chrc(struct gatt_db_attribute *attr, void *user_data)
+{
+ struct gatt_saver *saver = user_data;
+ GKeyFile *key_file = saver->key_file;
+ char handle[6], value[100], uuid_str[MAX_LEN_UUID_STR];
+ uint16_t handle_num, value_handle;
+ uint8_t properties;
+ bt_uuid_t uuid;
+
+ if (!gatt_db_attribute_get_char_data(attr, &handle_num, &value_handle,
+ &properties, &uuid)) {
+ warn("Error storing characteristic - can't get data");
+ return;
+ }
+
+ sprintf(handle, "%04hx", handle_num);
+ bt_uuid_to_string(&uuid, uuid_str, sizeof(uuid_str));
+ sprintf(value, GATT_CHARAC_UUID_STR ":%04hx:%02hx:%s", value_handle,
+ properties, uuid_str);
+ g_key_file_set_string(key_file, "Attributes", handle, value);
+
+ gatt_db_service_foreach_desc(attr, store_desc, saver);
+}
+
+static void store_incl(struct gatt_db_attribute *attr, void *user_data)
+{
+ struct gatt_saver *saver = user_data;
+ GKeyFile *key_file = saver->key_file;
+ struct gatt_db_attribute *service;
+ char handle[6], value[100], uuid_str[MAX_LEN_UUID_STR];
+ uint16_t handle_num, start, end;
+ bt_uuid_t uuid;
+
+ if (!gatt_db_attribute_get_incl_data(attr, &handle_num, &start, &end)) {
+ warn("Error storing included service - can't get data");
+ return;
+ }
+
+ service = gatt_db_get_attribute(saver->device->db, start);
+ if (!service) {
+ warn("Error storing included service - can't find it");
+ return;
+ }
+
+ sprintf(handle, "%04hx", handle_num);
+
+ bt_uuid_to_string(&uuid, uuid_str, sizeof(uuid_str));
+ sprintf(value, GATT_INCLUDE_UUID_STR ":%04hx:%04hx:%s", start,
+ end, uuid_str);
+
+ g_key_file_set_string(key_file, "Attributes", handle, value);
+}
+
+static void store_service(struct gatt_db_attribute *attr, void *user_data)
+{
+ struct gatt_saver *saver = user_data;
+ GKeyFile *key_file = saver->key_file;
+ char uuid_str[MAX_LEN_UUID_STR], handle[6], value[256];
+ uint16_t start, end;
+ bt_uuid_t uuid;
+ bool primary;
+ char *type;
+
+ if (!gatt_db_attribute_get_service_data(attr, &start, &end, &primary,
+ &uuid)) {
+ warn("Error storing service - can't get data");
+ return;
+ }
+
+ sprintf(handle, "%04hx", start);
+
+ bt_uuid_to_string(&uuid, uuid_str, sizeof(uuid_str));
+
+ if (primary)
+ type = GATT_PRIM_SVC_UUID_STR;
+ else
+ type = GATT_SND_SVC_UUID_STR;
+
+ sprintf(value, "%s:%04hx:%s", type, end, uuid_str);
+
+ g_key_file_set_string(key_file, "Attributes", handle, value);
+
+ gatt_db_service_foreach_incl(attr, store_incl, saver);
+ gatt_db_service_foreach_char(attr, store_chrc, saver);
+}
+
+static void store_gatt_db(struct btd_device *device)
+{
+ struct btd_adapter *adapter = device->adapter;
+ char filename[PATH_MAX];
+ char src_addr[18], dst_addr[18];
+ GKeyFile *key_file;
+ char *data;
+ gsize length = 0;
+ struct gatt_saver saver;
+
+ if (device_address_is_private(device)) {
+ warn("Can't store GATT db for private addressed device %s",
+ device->path);
+ return;
+ }
+
+ ba2str(btd_adapter_get_address(adapter), src_addr);
+ ba2str(&device->bdaddr, dst_addr);
+
+ snprintf(filename, PATH_MAX, STORAGEDIR "/%s/cache/%s", src_addr,
+ dst_addr);
+ create_file(filename, S_IRUSR | S_IWUSR);
+
+ key_file = g_key_file_new();
+ g_key_file_load_from_file(key_file, filename, 0, NULL);
+
+ saver.key_file = key_file;
+ saver.device = device;
+
+ gatt_db_foreach_service(device->db, NULL, store_service, &saver);
+
+ data = g_key_file_to_data(key_file, &length, NULL);
+ g_file_set_contents(filename, data, length, NULL);
+
+ g_free(data);
+ g_key_file_free(key_file);
+}
+
+
static void browse_request_complete(struct browse_req *req, uint8_t bdaddr_type,
int err)
{
@@ -2788,6 +2940,256 @@
*new_services = g_slist_append(*new_services, prim);
}
+static int load_desc(char *handle, char *value,
+ struct gatt_db_attribute *service)
+{
+ char uuid_str[MAX_LEN_UUID_STR];
+ struct gatt_db_attribute *att;
+ uint16_t handle_int;
+ bt_uuid_t uuid;
+
+ if (sscanf(handle, "%04hx", &handle_int) != 1)
+ return -EIO;
+
+ if (sscanf(value, "%s", uuid_str) != 1)
+ return -EIO;
+
+ bt_string_to_uuid(&uuid, uuid_str);
+
+ DBG("loading descriptor handle: 0x%04x, uuid: %s", handle_int,
+ uuid_str);
+
+ att = gatt_db_service_insert_descriptor(service, handle_int, &uuid,
+ 0, NULL, NULL, NULL);
+ if (!att || gatt_db_attribute_get_handle(att) != handle_int) {
+ warn("loading descriptor to db failed");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int load_chrc(char *handle, char *value,
+ struct gatt_db_attribute *service)
+{
+ uint16_t properties, value_handle, handle_int;
+ char uuid_str[MAX_LEN_UUID_STR];
+ struct gatt_db_attribute *att;
+ bt_uuid_t uuid;
+
+ if (sscanf(handle, "%04hx", &handle_int) != 1)
+ return -EIO;
+
+ if (sscanf(value, GATT_CHARAC_UUID_STR ":%04hx:%02hx:%s", &value_handle,
+ &properties, uuid_str) != 3)
+ return -EIO;
+
+ bt_string_to_uuid(&uuid, uuid_str);
+
+ /* Log debug message. */
+ DBG("loading characteristic handle: 0x%04x, value handle: 0x%04x,"
+ " properties 0x%04x uuid: %s", handle_int,
+ value_handle, properties, uuid_str);
+
+ att = gatt_db_service_insert_characteristic(service, value_handle,
+ &uuid, 0, properties,
+ NULL, NULL, NULL);
+ if (!att || gatt_db_attribute_get_handle(att) != value_handle) {
+ warn("saving characteristic to db failed");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int load_incl(struct gatt_db *db, char *handle, char *value,
+ struct gatt_db_attribute *service)
+{
+ char uuid_str[MAX_LEN_UUID_STR];
+ struct gatt_db_attribute *att;
+ uint16_t start, end;
+
+ if (sscanf(handle, "%04hx", &start) != 1)
+ return -EIO;
+
+ if (sscanf(value, GATT_INCLUDE_UUID_STR ":%04hx:%04hx:%s", &start, &end,
+ uuid_str) != 3)
+ return -EIO;
+
+ /* Log debug message. */
+ DBG("loading included service: 0x%04x, end: 0x%04x, uuid: %s", start,
+ end, uuid_str);
+
+ att = gatt_db_get_attribute(db, start);
+ if (!att) {
+ warn("saving included service to db failed - no such service");
+ return -EIO;
+ }
+
+ att = gatt_db_service_add_included(service, att);
+ if (!att) {
+ warn("saving included service to db failed");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int load_service(struct gatt_db *db, char *handle, char *value)
+{
+ struct gatt_db_attribute *att;
+ uint16_t start, end;
+ char type[MAX_LEN_UUID_STR], uuid_str[MAX_LEN_UUID_STR];
+ bt_uuid_t uuid;
+ bool primary;
+
+ if (sscanf(handle, "%04hx", &start) != 1)
+ return -EIO;
+
+ if (sscanf(value, "%[^:]:%04hx:%s", type, &end, uuid_str) != 3)
+ return -EIO;
+
+ if (g_str_equal(type, GATT_PRIM_SVC_UUID_STR))
+ primary = true;
+ else if (g_str_equal(type, GATT_SND_SVC_UUID_STR))
+ primary = false;
+ else
+ return -EIO;
+
+ bt_string_to_uuid(&uuid, uuid_str);
+
+ /* Log debug message. */
+ DBG("loading service: 0x%04x, end: 0x%04x, uuid: %s",
+ start, end, uuid_str);
+
+ att = gatt_db_insert_service(db, start, &uuid, primary,
+ end - start + 1);
+ if (!att) {
+ DBG("ERROR saving service to db!");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int load_gatt_db_impl(GKeyFile *key_file, char **keys,
+ struct gatt_db *db)
+{
+ struct gatt_db_attribute *current_service;
+ char **handle, *value, type[MAX_LEN_UUID_STR];
+ int ret;
+
+ /* first load service definitions */
+ for (handle = keys; *handle; handle++) {
+ value = g_key_file_get_string(key_file, "Attributes", *handle,
+ NULL);
+
+ if (sscanf(value, "%[^:]:", type) != 1) {
+ warn("Missing Type in attribute definition");
+ g_free(value);
+ return -EIO;
+ }
+
+ if (g_str_equal(type, GATT_PRIM_SVC_UUID_STR) ||
+ g_str_equal(type, GATT_SND_SVC_UUID_STR)) {
+ ret = load_service(db, *handle, value);
+ if (ret) {
+ g_free(value);
+ return ret;
+ }
+ }
+
+ g_free(value);
+ }
+
+ current_service = NULL;
+ /* then fill them with data*/
+ for (handle = keys; *handle; handle++) {
+ value = g_key_file_get_string(key_file, "Attributes", *handle,
+ NULL);
+
+ if (sscanf(value, "%[^:]:", type) != 1) {
+ warn("Missing Type in attribute definition");
+ g_free(value);
+ return -EIO;
+ }
+
+ if (g_str_equal(type, GATT_PRIM_SVC_UUID_STR) ||
+ g_str_equal(type, GATT_SND_SVC_UUID_STR)) {
+ uint16_t tmp;
+ uint16_t start, end;
+ bool primary;
+ bt_uuid_t uuid;
+ char uuid_str[MAX_LEN_UUID_STR];
+
+ if (sscanf(*handle, "%04hx", &tmp) != 1) {
+ warn("Unable to parse attribute handle");
+ g_free(value);
+ return -EIO;
+ }
+
+ if (current_service)
+ gatt_db_service_set_active(current_service,
+ true);
+
+ current_service = gatt_db_get_attribute(db, tmp);
+
+ gatt_db_attribute_get_service_data(current_service,
+ &start, &end,
+ &primary, &uuid);
+
+ bt_uuid_to_string(&uuid, uuid_str, sizeof(uuid_str));
+ } else if (g_str_equal(type, GATT_INCLUDE_UUID_STR)) {
+ ret = load_incl(db, *handle, value, current_service);
+ } else if (g_str_equal(type, GATT_CHARAC_UUID_STR)) {
+ ret = load_chrc(*handle, value, current_service);
+ } else {
+ ret = load_desc(*handle, value, current_service);
+ }
+
+ g_free(value);
+ if (ret)
+ return ret;
+ }
+
+ if (current_service)
+ gatt_db_service_set_active(current_service, true);
+
+ return 0;
+}
+
+static void load_gatt_db(struct btd_device *device, const char *local,
+ const char *peer)
+{
+ char **keys, filename[PATH_MAX];
+ GKeyFile *key_file;
+
+ DBG("Restoring %s gatt database from file", peer);
+
+ snprintf(filename, PATH_MAX, STORAGEDIR "/%s/cache/%s", local, peer);
+
+ key_file = g_key_file_new();
+ g_key_file_load_from_file(key_file, filename, 0, NULL);
+ keys = g_key_file_get_keys(key_file, "Attributes", NULL, NULL);
+
+ if (!keys) {
+ warn("No cache for %s", peer);
+ g_key_file_free(key_file);
+ return;
+ }
+
+ if (load_gatt_db_impl(key_file, keys, device->db))
+ warn("Unable to load gatt db from file for %s", peer);
+
+ g_strfreev(keys);
+ g_key_file_free(key_file);
+
+ g_slist_free_full(device->primaries, g_free);
+ device->primaries = NULL;
+ gatt_db_foreach_service(device->db, NULL, add_primary,
+ &device->primaries);
+}
+
static void device_add_uuids(struct btd_device *device, GSList *uuids)
{
GSList *l;
@@ -2810,14 +3212,6 @@
DEVICE_INTERFACE, "UUIDs");
}
-struct gatt_probe_data {
- struct btd_device *dev;
- bool all_services;
- GSList *uuids;
- struct gatt_db_attribute *cur_attr;
- char cur_uuid[MAX_LEN_UUID_STR];
-};
-
static bool device_match_profile(struct btd_device *device,
struct btd_profile *profile,
GSList *uuids)
@@ -2832,89 +3226,42 @@
return true;
}
-static void dev_probe_gatt(struct btd_profile *p, void *user_data)
+static void probe_gatt_profile(struct gatt_db_attribute *attr, void *user_data)
{
- struct gatt_probe_data *data = user_data;
+ struct btd_device *device = user_data;
struct btd_service *service;
-
- if (p->device_probe == NULL)
- return;
-
- if (!p->remote_uuid || bt_uuid_strcmp(p->remote_uuid, data->cur_uuid))
- return;
-
- service = service_create(data->dev, p);
- if (!service)
- return;
-
- if (service_probe(service) < 0) {
- btd_service_unref(service);
- return;
- }
-
- /* Mark service as active to skip discovering it again */
- gatt_db_service_set_active(data->cur_attr, true);
-
- /* Mark service as claimed */
- gatt_db_service_set_claimed(data->cur_attr, true);
-
- data->dev->services = g_slist_append(data->dev->services, service);
-}
-
-static void dev_probe_gatt_profile(struct gatt_db_attribute *attr,
- void *user_data)
-{
- struct gatt_probe_data *data = user_data;
+ struct btd_profile *profile;
bt_uuid_t uuid;
- GSList *l = NULL;
+ char uuid_str[MAX_LEN_UUID_STR];
+ GSList *l;
gatt_db_attribute_get_service_uuid(attr, &uuid);
- bt_uuid_to_string(&uuid, data->cur_uuid, sizeof(data->cur_uuid));
+ bt_uuid_to_string(&uuid, uuid_str, sizeof(uuid_str));
- data->cur_attr = attr;
+ /* Add UUID and probe service */
+ btd_device_add_uuid(device, uuid_str);
- /*
- * If we're probing for all services, store the UUID since device->uuids
- * was cleared.
- */
- if (data->all_services)
- data->uuids = g_slist_append(data->uuids,
- g_strdup(data->cur_uuid));
-
- /* Don't probe the profiles if a matching service already exists. */
- if (find_service_with_uuid(data->dev->services, data->cur_uuid)) {
- /* Mark service as active to skip discovering it again */
- gatt_db_service_set_active(data->cur_attr, true);
- /* Mark the service as claimed by the existing profile. */
- gatt_db_service_set_claimed(data->cur_attr, true);
- return;
- }
-
- btd_profile_foreach(dev_probe_gatt, data);
-
- if (data->all_services)
+ /* Check if service was probed */
+ l = find_service_with_uuid(device->services, uuid_str);
+ if (!l)
return;
- l = g_slist_append(l, g_strdup(data->cur_uuid));
- device_add_uuids(data->dev, l);
-}
+ /* Mark service as active to skip discovering it again */
+ gatt_db_service_set_active(attr, true);
-static void device_probe_gatt_profile(struct btd_device *device,
- struct gatt_db_attribute *attr)
-{
- struct gatt_probe_data data;
+ service = l->data;
+ profile = btd_service_get_profile(service);
- memset(&data, 0, sizeof(data));
+ /* Don't claim attributes of external profiles */
+ if (profile->external)
+ return;
- data.dev = device;
-
- dev_probe_gatt_profile(attr, &data);
- g_slist_free_full(data.uuids, g_free);
+ /* Mark the service as claimed by the existing profile. */
+ gatt_db_service_set_claimed(attr, true);
}
static void device_probe_gatt_profiles(struct btd_device *device)
{
- struct gatt_probe_data data;
char addr[18];
ba2str(&device->bdaddr, addr);
@@ -2924,16 +3271,7 @@
return;
}
- memset(&data, 0, sizeof(data));
-
- data.dev = device;
- data.all_services = true;
-
- gatt_db_foreach_service(device->db, NULL, dev_probe_gatt_profile,
- &data);
-
- device_add_uuids(device, data.uuids);
- g_slist_free_full(data.uuids, g_free);
+ gatt_db_foreach_service(device->db, NULL, probe_gatt_profile, device);
}
static void device_accept_gatt_profiles(struct btd_device *device)
@@ -2969,6 +3307,8 @@
{
struct btd_device *device = user_data;
+ store_gatt_db(device);
+
g_dbus_emit_property_changed(dbus_conn, device->path, DEVICE_INTERFACE,
"GattServices");
@@ -3014,9 +3354,7 @@
service_accept(l->data);
}
- device_probe_gatt_profile(device, attr);
-
- store_device_info(device);
+ btd_device_add_uuid(device, uuid_str);
btd_gatt_client_service_added(device->client_dbus, attr);
@@ -3631,24 +3969,40 @@
GSList *uuids;
};
+static struct btd_service *probe_service(struct btd_device *device,
+ struct btd_profile *profile,
+ GSList *uuids)
+{
+ struct btd_service *service;
+
+ if (profile->device_probe == NULL)
+ return NULL;
+
+ if (!device_match_profile(device, profile, uuids))
+ return NULL;
+
+ service = service_create(device, profile);
+
+ if (service_probe(service)) {
+ btd_service_unref(service);
+ return NULL;
+ }
+
+ if (profile->auto_connect)
+ device_set_auto_connect(device, TRUE);
+
+ return service;
+}
+
static void dev_probe(struct btd_profile *p, void *user_data)
{
struct probe_data *d = user_data;
struct btd_service *service;
- if (p->device_probe == NULL)
+ service = probe_service(d->dev, p, d->uuids);
+ if (!service)
return;
- if (!device_match_profile(d->dev, p, d->uuids))
- return;
-
- service = service_create(d->dev, p);
-
- if (service_probe(service) < 0) {
- btd_service_unref(service);
- return;
- }
-
d->dev->services = g_slist_append(d->dev->services, service);
}
@@ -3658,19 +4012,10 @@
struct btd_profile *profile = b;
struct btd_service *service;
- if (profile->device_probe == NULL)
+ service = probe_service(device, profile, device->uuids);
+ if (!service)
return;
- if (!device_match_profile(device, profile, device->uuids))
- return;
-
- service = service_create(device, profile);
-
- if (service_probe(service) < 0) {
- btd_service_unref(service);
- return;
- }
-
device->services = g_slist_append(device->services, service);
if (!profile->auto_connect || !device->general_connect)
@@ -4253,6 +4598,8 @@
}
device->gatt_cache_used = !gatt_db_isempty(device->db);
+
+ btd_gatt_client_connected(device->client_dbus);
}
static void gatt_server_init(struct btd_device *device, struct gatt_db *db)
@@ -4307,6 +4654,8 @@
uint16_t mtu;
uint16_t cid;
struct btd_gatt_database *database;
+ const bdaddr_t *src, *dst;
+ char srcaddr[18], dstaddr[18];
bt_io_get(io, &gerr, BT_IO_OPT_SEC_LEVEL, &sec_level,
BT_IO_OPT_IMTU, &mtu,
@@ -4358,6 +4707,15 @@
database = btd_adapter_get_database(dev->adapter);
+ src = btd_adapter_get_address(dev->adapter);
+ ba2str(src, srcaddr);
+
+ dst = device_get_address(dev);
+ ba2str(dst, dstaddr);
+
+ if (gatt_db_isempty(dev->db))
+ load_gatt_db(dev, srcaddr, dstaddr);
+
gatt_client_init(dev);
gatt_server_init(dev, btd_gatt_database_get_db(database));
@@ -4953,6 +5311,10 @@
* request
*/
if (state->svc_resolved && bonding) {
+ /* Attept to store services for this device failed because it
+ * was not paired. Now that we're paired retry. */
+ store_gatt_db(device);
+
g_dbus_send_reply(dbus_conn, bonding->msg, DBUS_TYPE_INVALID);
bonding_request_free(bonding);
return;
diff --git a/src/gatt-client.c b/src/gatt-client.c
index 9e6e47b..0408e17 100644
--- a/src/gatt-client.c
+++ b/src/gatt-client.c
@@ -520,7 +520,7 @@
{
struct descriptor *desc = data;
- desc->write_id = false;
+ desc->write_id = 0;
/*
* The descriptor might have been unregistered during the read. Return
@@ -773,8 +773,10 @@
if (err)
return;
- g_dbus_emit_property_changed(btd_get_dbus_connection(), chrc->path,
- GATT_CHARACTERISTIC_IFACE, "Value");
+ g_dbus_emit_property_changed_full(btd_get_dbus_connection(),
+ chrc->path, GATT_CHARACTERISTIC_IFACE,
+ "Value", G_DBUS_PROPERTY_CHANGED_FLAG_FLUSH);
+
}
static void chrc_read_cb(bool success, uint8_t att_ecode, const uint8_t *value,
@@ -867,7 +869,7 @@
{
struct characteristic *chrc = data;
- chrc->write_id = false;
+ chrc->write_id = 0;
/*
* The characteristic might have been unregistered during the read.
@@ -941,11 +943,11 @@
goto fail;
supported = true;
- chrc->write_id = bt_gatt_client_write_without_response(gatt,
+
+ if (bt_gatt_client_write_without_response(gatt,
chrc->value_handle,
chrc->props & BT_GATT_CHRC_PROP_AUTH,
- value, value_len);
- if (chrc->write_id)
+ value, value_len))
return dbus_message_new_method_return(msg);
fail:
@@ -1521,6 +1523,9 @@
/* Set service active so we can skip discovering next time */
gatt_db_service_set_active(attr, true);
+ /* Mark the service as claimed since it going to be exported */
+ gatt_db_service_set_claimed(attr, true);
+
return service;
}
@@ -1805,30 +1810,36 @@
void btd_gatt_client_ready(struct btd_gatt_client *client)
{
- struct bt_gatt_client *gatt;
-
if (!client)
return;
+ if (!client->gatt) {
+ error("GATT client not initialized");
+ return;
+ }
+
+ client->ready = true;
+
+ DBG("GATT client ready");
+
+ create_services(client);
+}
+
+void btd_gatt_client_connected(struct btd_gatt_client *client)
+{
+ struct bt_gatt_client *gatt;
+
gatt = btd_device_get_gatt_client(client->device);
if (!gatt) {
error("GATT client not initialized");
return;
}
+ DBG("Device connected.");
+
bt_gatt_client_unref(client->gatt);
client->gatt = bt_gatt_client_ref(gatt);
- client->ready = true;
-
- DBG("GATT client ready");
-
- if (queue_isempty(client->services)) {
- DBG("Exporting services");
- create_services(client);
- return;
- }
-
/*
* Services have already been created before. Re-enable notifications
* for any pre-registered notification sessions.
diff --git a/src/gatt-client.h b/src/gatt-client.h
index a18db17..92a9255 100644
--- a/src/gatt-client.h
+++ b/src/gatt-client.h
@@ -23,6 +23,7 @@
void btd_gatt_client_destroy(struct btd_gatt_client *client);
void btd_gatt_client_ready(struct btd_gatt_client *client);
+void btd_gatt_client_connected(struct btd_gatt_client *client);
void btd_gatt_client_service_added(struct btd_gatt_client *client,
struct gatt_db_attribute *attrib);
void btd_gatt_client_service_removed(struct btd_gatt_client *client,
diff --git a/src/gatt-database.c b/src/gatt-database.c
index 41282e7..a0e4ac0 100644
--- a/src/gatt-database.c
+++ b/src/gatt-database.c
@@ -43,6 +43,7 @@
#include "gatt-database.h"
#include "dbus-common.h"
#include "profile.h"
+#include "service.h"
#ifndef ATT_CID
#define ATT_CID 4
@@ -383,6 +384,7 @@
DBG("Removed \"%s\"", p->name);
adapter_foreach(adapter_remove_profile, p);
+ btd_profile_unregister(p);
g_free((void *) p->name);
g_free((void *) p->remote_uuid);
@@ -1073,7 +1075,7 @@
service_remove_helper(user_data);
}
-static void service_remove(void *data)
+static void remove_service(void *data)
{
struct external_service *service = data;
@@ -1452,7 +1454,7 @@
DBG("Proxy removed - removing service: %s", service->path);
- service_remove(service);
+ remove_service(service);
}
static bool parse_uuid(GDBusProxy *proxy, bt_uuid_t *uuid)
@@ -1783,22 +1785,16 @@
return 0;
}
- /*
- * TODO: All of the errors below should fall into the so called
- * "Application Error" range. Since there is no well defined error for
- * these, we return a generic ATT protocol error for now.
- */
-
if (chrc->ntfy_cnt == UINT_MAX) {
/* Maximum number of per-device CCC descriptors configured */
- return BT_ATT_ERROR_REQUEST_NOT_SUPPORTED;
+ return BT_ATT_ERROR_INSUFFICIENT_RESOURCES;
}
/* Don't support undefined CCC values yet */
if (value > 2 ||
(value == 1 && !(chrc->props & BT_GATT_CHRC_PROP_NOTIFY)) ||
(value == 2 && !(chrc->props & BT_GATT_CHRC_PROP_INDICATE)))
- return BT_ATT_ERROR_REQUEST_NOT_SUPPORTED;
+ return BT_ERROR_CCC_IMPROPERLY_CONFIGURED;
/*
* Always call StartNotify for an incoming enable and ignore the return
@@ -1807,7 +1803,7 @@
if (g_dbus_proxy_method_call(chrc->proxy,
"StartNotify", NULL, NULL,
NULL, NULL) == FALSE)
- return BT_ATT_ERROR_REQUEST_NOT_SUPPORTED;
+ return BT_ATT_ERROR_UNLIKELY;
__sync_fetch_and_add(&chrc->ntfy_cnt, 1);
@@ -2147,10 +2143,10 @@
service->reg = NULL;
if (fail)
- service_remove(service);
+ remove_service(service);
}
-static struct external_service *service_create(DBusConnection *conn,
+static struct external_service *create_service(DBusConnection *conn,
DBusMessage *msg, const char *path)
{
struct external_service *service;
@@ -2228,7 +2224,7 @@
if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_ARRAY)
return btd_error_invalid_args(msg);
- service = service_create(conn, msg, path);
+ service = create_service(conn, msg, path);
if (!service)
return btd_error_failed(msg, "Failed to register service");
@@ -2284,6 +2280,22 @@
profile_free(profile);
}
+static int profile_device_probe(struct btd_service *service)
+{
+ struct btd_profile *p = btd_service_get_profile(service);
+
+ DBG("%s probed", p->name);
+
+ return 0;
+}
+
+static void profile_device_remove(struct btd_service *service)
+{
+ struct btd_profile *p = btd_service_get_profile(service);
+
+ DBG("%s removed", p->name);
+}
+
static int profile_add(struct external_profile *profile, const char *uuid)
{
struct btd_profile *p;
@@ -2307,7 +2319,10 @@
return -ENOMEM;
}
+ p->device_probe = profile_device_probe;
+ p->device_remove = profile_device_remove;
p->auto_connect = true;
+ p->external = true;
queue_push_tail(profile->profiles, p);
@@ -2320,6 +2335,7 @@
{
struct btd_adapter *adapter = user_data;
+ btd_profile_register(data);
adapter_add_profile(adapter, data);
}
diff --git a/src/main.conf b/src/main.conf
index 11db383..372fd8c 100644
--- a/src/main.conf
+++ b/src/main.conf
@@ -1,10 +1,8 @@
[General]
# Default adaper name
-# %h - substituted for hostname
-# %d - substituted for adapter id
-# Defaults to 'BlueZ'
-#Name = %h-%d
+# Defaults to 'BlueZ X.YZ'
+#Name = BlueZ
# Default device class. Only the major and minor device class bits are
# considered. Defaults to '0x000000'.
@@ -84,3 +82,8 @@
# If the number of attempts defined in ReconnectAttempts is bigger than the
# set of intervals the last interval is repeated until the last attempt.
#ReconnectIntervals=1, 2, 4, 8, 16, 32, 64
+
+# AutoEnable defines option to enable all controllers when they are found.
+# This includes adapters present on start as well as adapters that are plugged
+# in later on. Defaults to 'false'.
+#AutoEnable=false
diff --git a/src/profile.c b/src/profile.c
index 4b73026..70ee4c1 100644
--- a/src/profile.c
+++ b/src/profile.c
@@ -435,6 +435,9 @@
<attribute id=\"0x0317\"> \
<uint32 value=\"0x00000003\"/> \
</attribute> \
+ <attribute id=\"0x0200\"> \
+ <uint16 value=\"%u\" name=\"psm\"/> \
+ </attribute> \
</record>"
#define MAS_RECORD \
@@ -484,6 +487,9 @@
<attribute id=\"0x0317\"> \
<uint32 value=\"0x0000007f\"/> \
</attribute> \
+ <attribute id=\"0x0200\"> \
+ <uint16 value=\"%u\" name=\"psm\"/> \
+ </attribute> \
</record>"
#define MNS_RECORD \
@@ -524,12 +530,12 @@
<attribute id=\"0x0100\"> \
<text value=\"%s\"/> \
</attribute> \
- <attribute id=\"0x0200\"> \
- <uint16 value=\"%u\" name=\"psm\"/> \
- </attribute> \
<attribute id=\"0x0317\"> \
<uint32 value=\"0x0000007f\"/> \
</attribute> \
+ <attribute id=\"0x0200\"> \
+ <uint16 value=\"%u\" name=\"psm\"/> \
+ </attribute> \
</record>"
#define SYNC_RECORD \
@@ -713,13 +719,19 @@
int btd_profile_register(struct btd_profile *profile)
{
- profiles = g_slist_append(profiles, profile);
+ if (profile->external)
+ ext_profiles = g_slist_append(ext_profiles, profile);
+ else
+ profiles = g_slist_append(profiles, profile);
return 0;
}
void btd_profile_unregister(struct btd_profile *profile)
{
- profiles = g_slist_remove(profiles, profile);
+ if (profile->external)
+ ext_profiles = g_slist_remove(ext_profiles, profile);
+ else
+ profiles = g_slist_remove(profiles, profile);
}
static struct ext_profile *find_ext_profile(const char *owner,
@@ -1792,15 +1804,29 @@
static char *get_pse_record(struct ext_profile *ext, struct ext_io *l2cap,
struct ext_io *rfcomm)
{
- return g_strdup_printf(PSE_RECORD, rfcomm->chan, ext->version,
- ext->name);
+ uint16_t psm = 0;
+ uint8_t chan = 0;
+
+ if (l2cap)
+ psm = l2cap->psm;
+ if (rfcomm)
+ chan = rfcomm->chan;
+
+ return g_strdup_printf(PSE_RECORD, chan, ext->version, ext->name, psm);
}
static char *get_mas_record(struct ext_profile *ext, struct ext_io *l2cap,
struct ext_io *rfcomm)
{
- return g_strdup_printf(MAS_RECORD, rfcomm->chan, ext->version,
- ext->name);
+ uint16_t psm = 0;
+ uint8_t chan = 0;
+
+ if (l2cap)
+ psm = l2cap->psm;
+ if (rfcomm)
+ chan = rfcomm->chan;
+
+ return g_strdup_printf(MAS_RECORD, chan, ext->version, ext->name, psm);
}
static char *get_mns_record(struct ext_profile *ext, struct ext_io *l2cap,
@@ -2001,6 +2027,8 @@
.uuid = OBEX_PSE_UUID,
.name = "Phone Book Access",
.channel = PBAP_DEFAULT_CHANNEL,
+ .psm = BTD_PROFILE_PSM_AUTO,
+ .mode = BT_IO_MODE_ERTM,
.authorize = true,
.get_record = get_pse_record,
.version = 0x0101,
@@ -2015,6 +2043,8 @@
.uuid = OBEX_MAS_UUID,
.name = "Message Access",
.channel = MAS_DEFAULT_CHANNEL,
+ .psm = BTD_PROFILE_PSM_AUTO,
+ .mode = BT_IO_MODE_ERTM,
.authorize = true,
.get_record = get_mas_record,
.version = 0x0100
@@ -2267,6 +2297,7 @@
p->name = ext->name;
p->local_uuid = ext->service ? ext->service : ext->uuid;
p->remote_uuid = ext->remote_uuid;
+ p->external = true;
if (ext->enable_server) {
p->adapter_probe = ext_adapter_probe;
diff --git a/src/profile.h b/src/profile.h
index f5a3ded..4448a2a 100644
--- a/src/profile.h
+++ b/src/profile.h
@@ -35,6 +35,7 @@
const char *remote_uuid;
bool auto_connect;
+ bool external;
int (*device_probe) (struct btd_service *service);
void (*device_remove) (struct btd_service *service);
diff --git a/src/shared/ad.c b/src/shared/ad.c
index 485bd7b..6259eb9 100644
--- a/src/shared/ad.c
+++ b/src/shared/ad.c
@@ -5,15 +5,19 @@
* Copyright (C) 2015 Google Inc.
*
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
+ * This 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 program is distributed in the hope that it will be useful,
+ * This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
+ * 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
*
*/
diff --git a/src/shared/ad.h b/src/shared/ad.h
index 4b313ab..709563d 100644
--- a/src/shared/ad.h
+++ b/src/shared/ad.h
@@ -5,15 +5,19 @@
* Copyright (C) 2015 Google Inc.
*
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
+ * This 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 program is distributed in the hope that it will be useful,
+ * This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
+ * 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
*
*/
diff --git a/src/shared/att.c b/src/shared/att.c
index 70f5d72..d88169e 100644
--- a/src/shared/att.c
+++ b/src/shared/att.c
@@ -82,7 +82,6 @@
void *debug_data;
struct bt_crypto *crypto;
- bool ext_signed;
struct sign_info *local_sign;
struct sign_info *remote_sign;
@@ -294,7 +293,7 @@
if (pdu_len > 1)
memcpy(op->pdu + 1, pdu, length);
- if (!sign || !(op->opcode & ATT_OP_SIGNED_MASK))
+ if (!sign || !(op->opcode & ATT_OP_SIGNED_MASK) || !att->crypto)
return true;
if (!sign->counter(&sign_cnt, sign->user_data))
@@ -782,7 +781,7 @@
const struct queue_entry *entry;
bool found;
- if ((opcode & ATT_OP_SIGNED_MASK) && !att->ext_signed) {
+ if ((opcode & ATT_OP_SIGNED_MASK) && !att->crypto) {
if (!handle_signed(att, opcode, pdu, pdu_len))
return;
pdu_len -= BT_ATT_SIGNATURE_LEN;
@@ -962,7 +961,6 @@
return NULL;
att->fd = fd;
- att->ext_signed = ext_signed;
att->mtu = BT_ATT_DEFAULT_LE_MTU;
att->buf = malloc(att->mtu);
if (!att->buf)
diff --git a/src/shared/btsnoop.c b/src/shared/btsnoop.c
index 3592c2e..2aa1b8d 100644
--- a/src/shared/btsnoop.c
+++ b/src/shared/btsnoop.c
@@ -242,6 +242,9 @@
case BTSNOOP_OPCODE_SCO_TX_PKT:
case BTSNOOP_OPCODE_SCO_RX_PKT:
break;
+ case BTSNOOP_OPCODE_OPEN_INDEX:
+ case BTSNOOP_OPCODE_CLOSE_INDEX:
+ break;
}
return 0xff;
diff --git a/src/shared/btsnoop.h b/src/shared/btsnoop.h
index 9675980..91b5922 100644
--- a/src/shared/btsnoop.h
+++ b/src/shared/btsnoop.h
@@ -43,6 +43,9 @@
#define BTSNOOP_OPCODE_ACL_RX_PKT 5
#define BTSNOOP_OPCODE_SCO_TX_PKT 6
#define BTSNOOP_OPCODE_SCO_RX_PKT 7
+#define BTSNOOP_OPCODE_OPEN_INDEX 8
+#define BTSNOOP_OPCODE_CLOSE_INDEX 9
+#define BTSNOOP_OPCODE_INDEX_INFO 10
#define BTSNOOP_MAX_PACKET_SIZE (1486 + 4)
@@ -53,6 +56,11 @@
char name[8];
} __attribute__((packed));
+struct btsnoop_opcode_index_info {
+ uint8_t bdaddr[6];
+ uint16_t manufacturer;
+} __attribute__((packed));
+
struct btsnoop;
struct btsnoop *btsnoop_open(const char *path, unsigned long flags);
diff --git a/src/shared/gatt-client.c b/src/shared/gatt-client.c
index 903afa7..d3e17e1 100644
--- a/src/shared/gatt-client.c
+++ b/src/shared/gatt-client.c
@@ -1596,8 +1596,7 @@
!notify_data->chrc->ccc_handle)
goto done;
- if (notify_data_write_ccc(notify_data, false, disable_ccc_callback))
- return;
+ notify_data_write_ccc(notify_data, false, disable_ccc_callback);
done:
notify_data_unref(notify_data);
@@ -2990,7 +2989,7 @@
if (!client || !client->db || !chrc_value_handle || !callback)
return 0;
- if (!bt_gatt_client_is_ready(client) || client->in_svc_chngd)
+ if (client->in_svc_chngd)
return 0;
return register_notify(client, chrc_value_handle, callback, notify,
diff --git a/src/shared/gatt-helpers.c b/src/shared/gatt-helpers.c
index c773df7..008b8bc 100644
--- a/src/shared/gatt-helpers.c
+++ b/src/shared/gatt-helpers.c
@@ -1490,6 +1490,7 @@
return;
success = false;
+ goto done;
}
success = true;
diff --git a/src/shared/tester.c b/src/shared/tester.c
index 30857e0..c3120fb 100644
--- a/src/shared/tester.c
+++ b/src/shared/tester.c
@@ -35,6 +35,10 @@
#include <glib.h>
+#ifdef HAVE_VALGRIND_MEMCHECK_H
+#include <valgrind/memcheck.h>
+#endif
+
#include "src/shared/util.h"
#include "src/shared/tester.h"
@@ -339,6 +343,10 @@
print_progress(test->name, COLOR_MAGENTA, "teardown");
test->teardown_func(test->test_data);
+#ifdef HAVE_VALGRIND_MEMCHECK_H
+ VALGRIND_DO_ADDED_LEAK_CHECK;
+#endif
+
return FALSE;
}
diff --git a/test/simple-player b/test/simple-player
index a8ae0b1..02754c2 100755
--- a/test/simple-player
+++ b/test/simple-player
@@ -43,6 +43,7 @@
self.properties = dbus.Dictionary({
"PlaybackStatus" : "playing",
+ "Identity" : "SimplePlayer",
"LoopStatus" : "None",
"Rate" : dbus.Double(1.0),
"Shuffle" : dbus.Boolean(False),
diff --git a/test/test-gatt-profile b/test/test-gatt-profile
new file mode 100755
index 0000000..ad320b1
--- /dev/null
+++ b/test/test-gatt-profile
@@ -0,0 +1,60 @@
+#!/usr/bin/python
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+from optparse import OptionParser, make_option
+import os
+import sys
+import uuid
+import dbus
+import dbus.service
+import dbus.mainloop.glib
+try:
+ from gi.repository import GObject
+except ImportError:
+ import gobject as GObject
+import bluezutils
+
+class GattProfile(dbus.service.Object):
+ @dbus.service.method("org.bluez.GattProfile1",
+ in_signature="", out_signature="")
+ def Release(self):
+ print("Release")
+ mainloop.quit()
+
+if __name__ == '__main__':
+ dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+ bus = dbus.SystemBus()
+
+ path = bluezutils.find_adapter().object_path
+
+ manager = dbus.Interface(bus.get_object("org.bluez", path),
+ "org.bluez.GattManager1")
+
+ option_list = [
+ make_option("-u", "--uuid", action="store",
+ type="string", dest="uuid",
+ default=None),
+ make_option("-p", "--path", action="store",
+ type="string", dest="path",
+ default="/foo/bar/profile"),
+ ]
+
+ opts = dbus.Dictionary({ }, signature='sv')
+
+ parser = OptionParser(option_list=option_list)
+
+ (options, args) = parser.parse_args()
+
+ profile = GattProfile(bus, options.path)
+
+ mainloop = GObject.MainLoop()
+
+ if not options.uuid:
+ options.uuid = str(uuid.uuid4())
+
+ uuids = { options.uuid }
+ manager.RegisterProfile(options.path, uuids, opts)
+
+ mainloop.run()
diff --git a/tools/btattach.c b/tools/btattach.c
index a9b892f..a025bb0 100644
--- a/tools/btattach.c
+++ b/tools/btattach.c
@@ -154,7 +154,8 @@
}
bt_hci_send(hci, BT_HCI_CMD_READ_LOCAL_VERSION, NULL, 0,
- local_version_callback, NULL, NULL);
+ local_version_callback, hci,
+ (bt_hci_destroy_func_t) bt_hci_unref);
}
return fd;
@@ -212,6 +213,7 @@
{ "ath3k", HCI_UART_ATH3K },
{ "intel", HCI_UART_INTEL },
{ "bcm", HCI_UART_BCM },
+ { "qca", HCI_UART_QCA },
{ }
};
diff --git a/tools/hciattach.c b/tools/hciattach.c
index 4dc5be5..59a76a7 100644
--- a/tools/hciattach.c
+++ b/tools/hciattach.c
@@ -1358,6 +1358,12 @@
dev[0] = 0;
if (!strchr(opt, '/'))
strcpy(dev, "/dev/");
+
+ if (strlen(opt) > PATH_MAX - (strlen(dev) + 1)) {
+ fprintf(stderr, "Invalid serial device\n");
+ exit(1);
+ }
+
strcat(dev, opt);
break;
diff --git a/tools/hciattach.h b/tools/hciattach.h
index 909ada8..4279a33 100644
--- a/tools/hciattach.h
+++ b/tools/hciattach.h
@@ -41,6 +41,7 @@
#define HCI_UART_ATH3K 5
#define HCI_UART_INTEL 6
#define HCI_UART_BCM 7
+#define HCI_UART_QCA 8
#define HCI_UART_RAW_DEVICE 0
#define HCI_UART_RESET_ON_INIT 1
diff --git a/tools/hciconfig.c b/tools/hciconfig.c
index 0e0b790..eac96b0 100644
--- a/tools/hciconfig.c
+++ b/tools/hciconfig.c
@@ -963,6 +963,8 @@
printf("%s, %s\n", major_devices[cls[1] & 0x1f],
get_minor_device_name(cls[1] & 0x1f, cls[0] >> 2));
}
+
+ hci_close_dev(hdev);
}
static void cmd_voice(int ctl, int hdev, char *opt)
@@ -1023,6 +1025,8 @@
}
printf("\tAir Coding Format: %s\n", acf[vs & 0x03]);
}
+
+ hci_close_dev(s);
}
static void cmd_delkey(int ctl, int hdev, char *opt)
@@ -1462,6 +1466,8 @@
printf("\tInquiry interval: %u slots (%.2f ms), window: %u slots (%.2f ms)\n",
interval, (float)interval * 0.625, window, (float)window * 0.625);
}
+
+ hci_close_dev(s);
}
static void cmd_page_parms(int ctl, int hdev, char *opt)
@@ -1533,6 +1539,8 @@
interval, (float)interval * 0.625,
window, (float)window * 0.625);
}
+
+ hci_close_dev(s);
}
static void cmd_page_to(int ctl, int hdev, char *opt)
@@ -1597,6 +1605,8 @@
printf("\tPage timeout: %u slots (%.2f ms)\n",
timeout, (float)timeout * 0.625);
}
+
+ hci_close_dev(s);
}
static void cmd_afh_mode(int ctl, int hdev, char *opt)
@@ -1630,6 +1640,8 @@
print_dev_hdr(&di);
printf("\tAFH mode: %s\n", mode == 1 ? "Enabled" : "Disabled");
}
+
+ hci_close_dev(dd);
}
static void cmd_ssp_mode(int ctl, int hdev, char *opt)
@@ -1664,6 +1676,8 @@
printf("\tSimple Pairing mode: %s\n",
mode == 1 ? "Enabled" : "Disabled");
}
+
+ hci_close_dev(dd);
}
static void print_rev_ericsson(int dd)
@@ -1790,6 +1804,9 @@
printf("\tUnsupported manufacturer\n");
break;
}
+
+ hci_close_dev(dd);
+
return;
}
diff --git a/tools/hcidump.c b/tools/hcidump.c
index 8839eb0..af8f592 100644
--- a/tools/hcidump.c
+++ b/tools/hcidump.c
@@ -145,7 +145,8 @@
struct frame frm;
struct pollfd fds[2];
int nfds = 0;
- char *buf, *ctrl;
+ char *buf;
+ char ctrl[100];
int len, hdr_size = HCIDUMP_HDR_SIZE;
if (sock < 0)
@@ -167,13 +168,6 @@
dp = (void *) buf;
frm.data = buf + hdr_size;
- ctrl = malloc(100);
- if (!ctrl) {
- free(buf);
- perror("Can't allocate control buffer");
- return -1;
- }
-
if (dev == HCI_DEV_NONE)
printf("system: ");
else
@@ -538,13 +532,13 @@
opt = 1;
if (setsockopt(sk, SOL_HCI, HCI_DATA_DIR, &opt, sizeof(opt)) < 0) {
perror("Can't enable data direction info");
- return -1;
+ goto fail;
}
opt = 1;
if (setsockopt(sk, SOL_HCI, HCI_TIME_STAMP, &opt, sizeof(opt)) < 0) {
perror("Can't enable time stamp");
- return -1;
+ goto fail;
}
/* Setup filter */
@@ -553,7 +547,7 @@
hci_filter_all_events(&flt);
if (setsockopt(sk, SOL_HCI, HCI_FILTER, &flt, sizeof(flt)) < 0) {
perror("Can't set filter");
- return -1;
+ goto fail;
}
/* Bind socket to the HCI device */
@@ -563,10 +557,14 @@
if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
printf("Can't attach to device hci%d. %s(%d)\n",
dev, strerror(errno), errno);
- return -1;
+ goto fail;
}
return sk;
+
+fail:
+ close(sk);
+ return -1;
}
static struct {
diff --git a/tools/l2test.c b/tools/l2test.c
index 386823f..d4e3ae6 100644
--- a/tools/l2test.c
+++ b/tools/l2test.c
@@ -592,6 +592,7 @@
if (socktype == SOCK_DGRAM) {
handler(sk);
+ close(sk);
return;
}
diff --git a/tools/mgmt-tester.c b/tools/mgmt-tester.c
index 9ace20a..0a1a7c2 100644
--- a/tools/mgmt-tester.c
+++ b/tools/mgmt-tester.c
@@ -4707,6 +4707,30 @@
.adv_data = adv_data_invalid_significant_len,
};
+static const uint8_t adv_data_invalid_field_len[] = { 0x02, 0x01, 0x01,
+ 0x05, 0x09, 0x74, 0x65, 0x73, 0x74,
+ 0xa0, 0xff, 0x01, 0x02, 0x03, 0x04, 0x05};
+
+static const char device_found_valid2[] = { 0x00, 0x00, 0x01, 0x01, 0xaa, 0x00,
+ 0x01, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x02, 0x01,
+ 0x01, 0x05, 0x09, 0x74, 0x65, 0x73, 0x74};
+
+static const struct generic_data device_found_invalid_field = {
+ .setup_settings = settings_powered_le,
+ .send_opcode = MGMT_OP_START_DISCOVERY,
+ .send_param = start_discovery_le_param,
+ .send_len = sizeof(start_discovery_le_param),
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_param = start_discovery_le_param,
+ .expect_len = sizeof(start_discovery_le_param),
+ .expect_alt_ev = MGMT_EV_DEVICE_FOUND,
+ .expect_alt_ev_param = device_found_valid2,
+ .expect_alt_ev_len = sizeof(device_found_valid2),
+ .set_adv = true,
+ .adv_data_len = sizeof(adv_data_invalid_field_len),
+ .adv_data = adv_data_invalid_field_len,
+};
+
static const struct generic_data read_local_oob_not_powered_test = {
.send_opcode = MGMT_OP_READ_LOCAL_OOB_DATA,
.expect_status = MGMT_STATUS_NOT_POWERED,
@@ -6957,9 +6981,12 @@
&read_local_oob_success_sc_test,
NULL, test_command_generic);
- test_bredrle("Device Found - Invalid remote ADV_DATA",
+ test_bredrle("Device Found - Advertising data - Zero padded",
&device_found_gtag,
NULL, test_device_found);
+ test_bredrle("Device Found - Advertising data - Invalid field",
+ &device_found_invalid_field,
+ NULL, test_device_found);
return tester_run();
}
diff --git a/tools/mpris-proxy.c b/tools/mpris-proxy.c
index 693055e..bf8148f 100644
--- a/tools/mpris-proxy.c
+++ b/tools/mpris-proxy.c
@@ -406,7 +406,7 @@
done:
dbus_message_unref(copy);
- return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ return DBUS_HANDLER_RESULT_HANDLED;
}
static struct player *find_player_by_bus_name(const char *name)
diff --git a/tools/rctest.c b/tools/rctest.c
index 9169e3c..6d84e07 100644
--- a/tools/rctest.c
+++ b/tools/rctest.c
@@ -102,7 +102,7 @@
static uint8_t get_channel(const char *svr, uint16_t uuid)
{
sdp_session_t *sdp;
- sdp_list_t *srch, *attrs, *rsp;
+ sdp_list_t *srch, *attrs, *rsp, *protos;
uuid_t svclass;
uint16_t attr;
bdaddr_t dst;
@@ -128,7 +128,6 @@
for (; rsp; rsp = rsp->next) {
sdp_record_t *rec = (sdp_record_t *) rsp->data;
- sdp_list_t *protos;
if (!sdp_get_access_protos(rec, &protos)) {
channel = sdp_get_proto_port(protos, RFCOMM_UUID);
@@ -137,7 +136,11 @@
}
}
+ sdp_list_free(protos, NULL);
+
done:
+ sdp_list_free(srch, NULL);
+ sdp_list_free(attrs, NULL);
sdp_close(sdp);
return channel;
@@ -593,6 +596,7 @@
syslog(LOG_INFO, "Close failed: %m");
else
syslog(LOG_INFO, "Done");
+ close(sk);
}
static void reconnect_mode(char *svr)
diff --git a/tools/sco-tester.c b/tools/sco-tester.c
index d55d51a..651fbe0 100644
--- a/tools/sco-tester.c
+++ b/tools/sco-tester.c
@@ -160,6 +160,7 @@
if (!data->hciemu) {
tester_warn("Failed to setup HCI emulation");
tester_pre_setup_failed();
+ return;
}
tester_print("New hciemu instance created");
diff --git a/tools/sdptool.c b/tools/sdptool.c
index cbe8814..b1cbcfd 100644
--- a/tools/sdptool.c
+++ b/tools/sdptool.c
@@ -3312,6 +3312,7 @@
sdp_record_t record;
sdp_list_t *root, *svclass;
uuid_t root_uuid, svclass_uuid;
+ int err;
memset(&record, 0, sizeof(record));
record.handle = si->handle;
@@ -3324,7 +3325,12 @@
svclass = sdp_list_append(NULL, &svclass_uuid);
sdp_set_service_classes(&record, svclass);
- if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+ err = sdp_device_record_register(session, &interface, &record,
+ SDP_RECORD_PERSIST);
+ sdp_list_free(root, NULL);
+ sdp_list_free(svclass, NULL);
+
+ if (err < 0) {
printf("Service Record registration failed\n");
return -1;
}
diff --git a/tools/valgrind.supp b/tools/valgrind.supp
new file mode 100644
index 0000000..bf28bcd
--- /dev/null
+++ b/tools/valgrind.supp
@@ -0,0 +1,14 @@
+{
+ ecb_bind
+ Memcheck:Param
+ socketcall.bind(my_addr.sa_data)
+ fun:bind
+ fun:ecb_aes_setup
+}
+{
+ cmac_bind
+ Memcheck:Param
+ socketcall.bind(my_addr.sa_data)
+ fun:bind
+ fun:cmac_aes_setup
+}
diff --git a/unit/test-gdbus-client.c b/unit/test-gdbus-client.c
index b25289f..dd17c00 100644
--- a/unit/test-gdbus-client.c
+++ b/unit/test-gdbus-client.c
@@ -861,6 +861,10 @@
context->timeout_source = g_timeout_add_seconds(2, timeout_test,
context);
+ g_dbus_detach_object_manager(conn);
+
+ g_dbus_unregister_interface(conn, SERVICE_PATH, SERVICE_NAME1);
+
dbus_connection_flush(conn);
dbus_connection_close(conn);
dbus_connection_unref(conn);
diff --git a/unit/test-hfp.c b/unit/test-hfp.c
index 66966ce..f2b9622 100644
--- a/unit/test-hfp.c
+++ b/unit/test-hfp.c
@@ -29,9 +29,9 @@
#include <glib.h>
#include "src/shared/hfp.h"
+#include "src/shared/tester.h"
struct context {
- GMainLoop *main_loop;
guint watch_id;
int fd_server;
int fd_client;
@@ -97,7 +97,7 @@
data.test_name = g_strdup(name); \
data.pdu_list = g_memdup(pdus, sizeof(pdus)); \
data.result_func = result_function; \
- g_test_add_data_func(name, &data, function); \
+ tester_add(name, &data, NULL, function, NULL); \
data.test_handler = test_handler; \
} while (0)
@@ -112,15 +112,10 @@
data.pdu_list = g_memdup(pdus, sizeof(pdus)); \
data.hf_result_func = result_func; \
data.response_func = response_function; \
- g_test_add_data_func(name, &data, function); \
+ tester_add(name, &data, NULL, function, NULL); \
data.test_handler = test_hf_handler; \
} while (0)
-static void context_quit(struct context *context)
-{
- g_main_loop_quit(context->main_loop);
-}
-
static void test_free(gconstpointer user_data)
{
const struct test_data *data = user_data;
@@ -129,6 +124,34 @@
g_free(data->pdu_list);
}
+static void destroy_context(struct context *context)
+{
+ if (context->watch_id)
+ g_source_remove(context->watch_id);
+
+ test_free(context->data);
+
+ if (context->hfp)
+ hfp_gw_unref(context->hfp);
+
+ if (context->hfp_hf)
+ hfp_hf_unref(context->hfp_hf);
+
+ g_free(context);
+}
+
+static gboolean context_quit(gpointer user_data)
+{
+ struct context *context = user_data;
+
+ if (context == NULL)
+ return FALSE;
+
+ destroy_context(context);
+ tester_test_passed();
+ return FALSE;
+}
+
static gboolean test_handler(GIOChannel *channel, GIOCondition cond,
gpointer user_data)
{
@@ -138,10 +161,10 @@
pdu = &context->data->pdu_list[context->pdu_offset++];
g_assert(!pdu->valid);
- context_quit(context);
-
context->watch_id = 0;
+ context_quit(context);
+
return FALSE;
}
@@ -184,9 +207,10 @@
return TRUE;
done:
- context_quit(context);
context->watch_id = 0;
+ context_quit(context);
+
return FALSE;
}
@@ -224,9 +248,6 @@
int err, sv[2];
const struct test_data *d = data;
- 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);
@@ -251,26 +272,6 @@
return context;
}
-static void execute_context(struct context *context)
-{
- g_main_loop_run(context->main_loop);
-
- if (context->watch_id)
- g_source_remove(context->watch_id);
-
- g_main_loop_unref(context->main_loop);
-
- test_free(context->data);
-
- if (context->hfp)
- hfp_gw_unref(context->hfp);
-
- if (context->hfp_hf)
- hfp_hf_unref(context->hfp_hf);
-
- g_free(context);
-}
-
static void test_init(gconstpointer data)
{
struct context *context = create_context(data);
@@ -283,7 +284,7 @@
hfp_gw_unref(context->hfp);
context->hfp = NULL;
- execute_context(context);
+ context_quit(context);
}
static void test_command_handler(gconstpointer data)
@@ -308,7 +309,7 @@
len = write(context->fd_server, pdu->data, pdu->size);
g_assert_cmpint(len, ==, pdu->size);
- execute_context(context);
+ context_quit(context);
}
static void test_register(gconstpointer data)
@@ -337,7 +338,7 @@
len = write(context->fd_server, pdu->data, pdu->size);
g_assert_cmpint(len, ==, pdu->size);
- execute_context(context);
+ context_quit(context);
}
static void test_fragmented(gconstpointer data)
@@ -352,8 +353,6 @@
g_assert(ret);
g_idle_add(send_pdu, context);
-
- execute_context(context);
}
static void test_send_and_close(gconstpointer data)
@@ -372,7 +371,7 @@
hfp_gw_unref(context->hfp);
context->hfp = NULL;
- execute_context(context);
+ context_quit(context);
}
static void check_ustring_1(struct hfp_context *result,
@@ -494,7 +493,7 @@
hfp_hf_unref(context->hfp_hf);
context->hfp_hf = NULL;
- execute_context(context);
+ context_quit(context);
}
static bool unsolicited_resp = false;
@@ -567,7 +566,7 @@
g_assert(ret);
}
- execute_context(context);
+ context_quit(context);
}
static void hf_chld_result_handler(struct hfp_context *hf_context,
void *user_data)
@@ -667,8 +666,6 @@
}
send_pdu(context);
-
- execute_context(context);
}
static void test_hf_robustness(gconstpointer data)
@@ -687,12 +684,12 @@
hfp_hf_unref(context->hfp_hf);
context->hfp_hf = NULL;
- execute_context(context);
+ context_quit(context);
}
int main(int argc, char *argv[])
{
- g_test_init(&argc, &argv, NULL);
+ tester_init(&argc, &argv);
define_test("/hfp/test_init", test_init, NULL, data_end());
define_test("/hfp/test_cmd_handler_1", test_command_handler, NULL,
@@ -860,5 +857,5 @@
frg_pdu('1', ',', '2', 'x', '\r', '\n'),
data_end());
- return g_test_run();
+ return tester_run();
}
diff --git a/unit/test-sdp.c b/unit/test-sdp.c
index b4ef4d1..ac921a9 100644
--- a/unit/test-sdp.c
+++ b/unit/test-sdp.c
@@ -37,6 +37,7 @@
#include "lib/sdp_lib.h"
#include "src/shared/util.h"
+#include "src/shared/tester.h"
#include "src/log.h"
#include "src/sdpd.h"
@@ -78,7 +79,7 @@
static struct test_data data; \
data.mtu = _mtu; \
data.pdu_list = g_memdup(pdus, sizeof(pdus)); \
- g_test_add_data_func(name, &data, test_sdp); \
+ tester_add(name, &data, NULL, test_sdp, NULL); \
} while (0)
#define define_ss(name, args...) define_test("/TP/SERVER/SS/" name, 48, args)
@@ -105,32 +106,48 @@
data.input_data = input; \
data.input_size = sizeof(input); \
data.expected = exp; \
- g_test_add_data_func("/sdp/DE/ATTR/" name, &data, \
- test_sdp_de_attr); \
+ tester_add("/sdp/DE/ATTR/" name, &data, NULL, \
+ test_sdp_de_attr, NULL); \
} while (0)
struct context {
- GMainLoop *main_loop;
guint server_source;
guint client_source;
int fd;
- int mtu;
uint8_t cont_data[16];
uint8_t cont_size;
unsigned int pdu_offset;
- const struct sdp_pdu *pdu_list;
+ const struct test_data *data;
};
static void sdp_debug(const char *str, void *user_data)
{
const char *prefix = user_data;
- g_print("%s%s\n", prefix, str);
+ tester_debug("%s%s\n", prefix, str);
}
-static void context_quit(struct context *context)
+static void destroy_context(struct context *context)
{
- g_main_loop_quit(context->main_loop);
+ sdp_svcdb_collect_all(context->fd);
+ sdp_svcdb_reset();
+
+ g_source_remove(context->server_source);
+ g_source_remove(context->client_source);
+
+ g_free(context);
+}
+
+static gboolean context_quit(gpointer user_data)
+{
+ struct context *context = user_data;
+ if (context == NULL)
+ return FALSE;
+
+ destroy_context(context);
+ tester_test_passed();
+
+ return FALSE;
}
static gboolean server_handler(GIOChannel *channel, GIOCondition cond,
@@ -169,10 +186,9 @@
return FALSE;
}
- if (g_test_verbose() == TRUE)
- util_hexdump('<', buf, len, sdp_debug, "SDP: ");
+ util_hexdump('<', buf, len, sdp_debug, "SDP: ");
- handle_internal_request(fd, context->mtu, buf, len);
+ handle_internal_request(fd, context->data->mtu, buf, len);
return TRUE;
}
@@ -185,7 +201,7 @@
unsigned char *buf;
ssize_t len;
- req_pdu = &context->pdu_list[context->pdu_offset];
+ req_pdu = &context->data->pdu_list[context->pdu_offset];
pdu_len = req_pdu->raw_size + context->cont_size;
@@ -210,7 +226,7 @@
{
context->pdu_offset += 2;
- if (!context->pdu_list[context->pdu_offset].valid) {
+ if (!context->data->pdu_list[context->pdu_offset].valid) {
context_quit(context);
return;
}
@@ -227,7 +243,7 @@
ssize_t len;
int fd;
- rsp_pdu = &context->pdu_list[context->pdu_offset + 1];
+ rsp_pdu = &context->data->pdu_list[context->pdu_offset + 1];
if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP))
return FALSE;
@@ -238,8 +254,7 @@
if (len < 0)
return FALSE;
- if (g_test_verbose() == TRUE)
- util_hexdump('>', buf, len, sdp_debug, "SDP: ");
+ util_hexdump('>', buf, len, sdp_debug, "SDP: ");
g_assert(len > 0);
g_assert((size_t) len == rsp_pdu->raw_size + rsp_pdu->cont_len);
@@ -638,15 +653,12 @@
update_db_timestamp();
}
-static struct context *create_context(void)
+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);
@@ -677,6 +689,7 @@
g_io_channel_unref(channel);
context->fd = sv[1];
+ context->data = data;
set_fixed_db_timestamp(0x496f0654);
@@ -695,34 +708,11 @@
return context;
}
-static void execute_context(struct context *context)
-{
- g_main_loop_run(context->main_loop);
-
- sdp_svcdb_collect_all(context->fd);
- sdp_svcdb_reset();
-
- g_source_remove(context->server_source);
- g_source_remove(context->client_source);
-
- g_main_loop_unref(context->main_loop);
-
- g_free(context);
-}
-
static void test_sdp(gconstpointer data)
{
- const struct test_data *test = data;
- struct context *context = create_context();
-
- context->mtu = test->mtu;
- context->pdu_list = test->pdu_list;
+ struct context *context = create_context(data);
g_idle_add(send_pdu, context);
-
- execute_context(context);
-
- g_free(test->pdu_list);
}
static void test_sdp_de_attr(gconstpointer data)
@@ -737,8 +727,7 @@
g_assert_cmpuint(test->input_size, ==, size);
g_assert_cmpuint(test->expected.dtd, ==, d->dtd);
- if (g_test_verbose() == TRUE)
- g_print("DTD=0x%02x\n", d->dtd);
+ tester_debug("DTD=0x%02x\n", d->dtd);
switch (d->dtd) {
case SDP_TEXT_STR8:
@@ -785,14 +774,14 @@
}
sdp_data_free(d);
+ tester_test_passed();
}
int main(int argc, char *argv[])
{
- g_test_init(&argc, &argv, NULL);
+ tester_init(&argc, &argv);
- if (g_test_verbose())
- __btd_log_init("*", 0);
+ __btd_log_init("*", 0);
/*
* Service Search Request
@@ -2821,5 +2810,5 @@
0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00)));
- return g_test_run();
+ return tester_run();
}
diff --git a/unit/test-uhid.c b/unit/test-uhid.c
index b48e0fa..320cd54 100644
--- a/unit/test-uhid.c
+++ b/unit/test-uhid.c
@@ -38,6 +38,8 @@
#include "src/shared/uhid.h"
#include "src/shared/util.h"
+#include "src/shared/tester.h"
+
struct test_pdu {
bool valid;
const uint8_t *data;
@@ -50,7 +52,6 @@
};
struct context {
- GMainLoop *main_loop;
struct bt_uhid *uhid;
guint source;
guint process;
@@ -74,14 +75,14 @@
static struct test_data data; \
data.test_name = g_strdup(name); \
data.pdu_list = g_memdup(pdus, sizeof(pdus)); \
- g_test_add_data_func(name, &data, function); \
+ tester_add(name, &data, NULL, function, NULL); \
} while (0)
static void test_debug(const char *str, void *user_data)
{
const char *prefix = user_data;
- g_print("%s%s\n", prefix, str);
+ tester_debug("%s%s\n", prefix, str);
}
static void test_free(gconstpointer user_data)
@@ -92,14 +93,29 @@
g_free(data->pdu_list);
}
+static void destroy_context(struct context *context)
+{
+ if (context->source > 0)
+ g_source_remove(context->source);
+
+ bt_uhid_unref(context->uhid);
+
+ test_free(context->data);
+ g_free(context);
+}
+
static gboolean context_quit(gpointer user_data)
{
struct context *context = user_data;
+ if (context == NULL)
+ return FALSE;
+
if (context->process > 0)
g_source_remove(context->process);
- g_main_loop_quit(context->main_loop);
+ destroy_context(context);
+ tester_test_passed();
return FALSE;
}
@@ -114,8 +130,8 @@
len = write(context->fd, pdu->data, pdu->size);
- if (g_test_verbose())
- util_hexdump('<', pdu->data, len, test_debug, "uHID: ");
+
+ util_hexdump('<', pdu->data, len, test_debug, "uHID: ");
g_assert_cmpint(len, ==, pdu->size);
@@ -156,8 +172,7 @@
g_assert(len > 0);
- if (g_test_verbose())
- util_hexdump('>', buf, len, test_debug, "uHID: ");
+ util_hexdump('>', buf, len, test_debug, "uHID: ");
g_assert_cmpint(len, ==, pdu->size);
@@ -174,9 +189,6 @@
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);
@@ -202,26 +214,6 @@
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,
};
@@ -263,7 +255,7 @@
if (g_str_equal(context->data->test_name, "/uhid/command/input"))
bt_uhid_send(context->uhid, &ev_input);
- execute_context(context);
+ context_quit(context);
}
static void handle_output(struct uhid_event *ev, void *user_data)
@@ -288,13 +280,11 @@
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);
+ tester_init(&argc, &argv);
define_test("/uhid/command/create", test_client, event(&ev_create));
define_test("/uhid/command/destroy", test_client, event(&ev_destroy));
@@ -305,5 +295,5 @@
define_test("/uhid/event/output", test_server, event(&ev_output));
define_test("/uhid/event/feature", test_server, event(&ev_feature));
- return g_test_run();
+ return tester_run();
}